zoukankan      html  css  js  c++  java
  • 题解:luogu P1073 最优贸易

    题目描述

    C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 1 条。

    C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。

    商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 C 国 n 个城市的标号从 1~ n,阿龙决定从 1 号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有 n 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。

    假设 C 国有 5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。

    假设 1~n 号城市的水晶球价格分别为 4,3,5,6,1。

    阿龙可以选择如下一条线路:1->2->3->5,并在 2 号城市以 3 的价格买入水晶球,在 3号城市以 5 的价格卖出水晶球,赚取的旅费数为 2。

    阿龙也可以选择如下一条线路 1->4->5->4->5,并在第 1 次到达 5 号城市时以 1 的价格买入水晶球,在第 2 次到达 4 号城市时以 6 的价格卖出水晶球,赚取的旅费数为 5。

    现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

    输入输出格式

    输入格式:

    第一行包含 2 个正整数 n 和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。

    第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。

    接下来 m 行,每行有 3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果 z=1,表示这条道路是城市 x 到城市 y 之间的单向道路;如果 z=2,表示这条道路为城市 x 和城市y 之间的双向道路。

    输出格式:

    输出文件 trade.out 共 1 行,包含 1 个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出 0。

    输入输出样例

    输入样例#1:
    5 5 
    4 3 5 6 1 
    1 2 1 
    1 4 1 
    2 3 2 
    3 5 1 
    4 5 2 
    输出样例#1: 
    5

    说明

    【数据范围】

    输入数据保证 1 号城市可以到达 n 号城市。

    对于 10%的数据,1≤n≤6。

    对于 30%的数据,1≤n≤100。

    对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。

    对于 100%的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市

    水晶球价格≤100。

    题解:

    实际上就是让你在图上任选两个点,求点权差最大的那一组,并且1,u,v,n四点联通。

    这道题的巧妙之处在于在求解过程中借用了类似DP的思路:对于一个点i,考虑从i到n所有合法路径上点权最大的那个点,其点权记为des[i],再考虑从1到i所有合法路径上点权最小的那个点,记为sta[i],那么最后只需要枚举每个点,取最大的(des[i]-sta[i])。

    剩下的问题就是如何求解des[i]与sta[i]

    可以考虑将点权转化为边权(将两个点之间的边权定义为终点的点权),接着在在图上跑两遍队列优化的dj即可。这里实际上是用dj来做DP:将在跑dj过程中的那个dis[]数组(即当前点到起点的距离)分别改为sta[i]与des[i],那么,在跑dj过程中,有:

                               des[i]=max(des[i],wi);

                        sta[i]=min(sta[i],wi);

    接着建两张图:一张正向图一张反向图,在正向图上求sta[i],在反向图上求des[i],这样就保证了答案的正确性:1,u,v,n四点联通且对于i,des[i]一定在其后面,sta[i]一定在其前面。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    struct front_star{
        int w,to,next;
    };
    struct sd1{
        int is,len;
    };
    priority_queue<sd1>que;
    bool operator< (sd1 a,sd1 b){  
        return a.len<b.len;  
    }
    struct sd2{
        int isx,lenx;
    };
    priority_queue<sd2>quex;
    bool operator< (sd2 a,sd2 b){  
        return a.lenx>b.lenx;  
    }    
    front_star edge1[500005],edge2[500005];
    int n,m,cnt=0,ans=0;
    int head1[100005],head2[100005],p[100005],des[100005],sta[100005];
    int maxn(int a,int b)
    {
        return a>b?a:b;
    }
    void addedge(int u,int v,int w1)
    {
        cnt++;
        edge1[cnt].to=v;
        edge1[cnt].w=w1;
        edge1[cnt].next=head1[u];
        head1[u]=cnt;
        edge2[cnt].to=u;
        edge2[cnt].w=w1;
        edge2[cnt].next=head2[v];
        head2[v]=cnt;
    }
    void clearx()
    {
        memset(head1,-1,sizeof(head1));
        memset(head2,-1,sizeof(head2));
        memset(des,0,sizeof(des));
        memset(sta,117,sizeof(sta));
    }
    int main()
    {
        clearx();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) 
            scanf("%d",&p[i]);
        for(int i=1;i<=m;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                if(c==1)
                   addedge(a,b,p[b]);
                else
                   {
                        addedge(a,b,p[b]);
                        addedge(b,a,p[a]);
                   }   
            }    
        des[n]=p[n];
        sd1 temp;
        temp.is=n;
        temp.len=des[n];
        que.push(temp);
        while(!que.empty())
              {
                  sd1 temp=que.top();
                  que.pop();
                  if(temp.len!=des[temp.is])
                     continue;
                  for(int i=head2[temp.is];~i;i=edge2[i].next)
                      {
                          int ct=edge2[i].to;
                          int w1=edge2[i].w;
                          if(des[ct]<w1)
                             {
                                 des[ct]=w1;
                                 sd1 tempx;
                                 tempx.is=ct;
                                 tempx.len=des[ct];
                                 que.push(tempx);
                             }
                      }   
              }
        sta[1]=p[1];
        sd2 tempp;
        tempp.isx=1;
        tempp.lenx=p[1];
        quex.push(tempp);
        while(!quex.empty())
              {
                  sd2 tempxx=quex.top();
                  quex.pop();
                  if(tempxx.lenx!=sta[tempxx.isx])
                     continue;
                  for(int i=head1[tempxx.isx];~i;i=edge1[i].next)
                      {
                          int ct=edge1[i].to;
                          int w1=edge1[i].w;
                          if(sta[ct]>w1)
                             {
                                 sta[ct]=w1;
                                 sd2 tempxp;
                                 tempxp.isx=ct;
                                 tempxp.lenx=sta[ct];
                                 quex.push(tempxp);
                             }
                      }   
              }
        for(int i=1;i<=n;i++)
            ans=maxn(ans,des[i]-sta[i]);
        printf("%d
    ",ans);                  
        return 0;
    }
    View Code
  • 相关阅读:
    December 23rd 2016 Week 52nd Friday
    December 22nd 2016 Week 52nd Thursday
    December 21st 2016 Week 52nd Wednesday
    December 20th 2016 Week 52nd Tuesday
    December 19th 2016 Week 52nd Sunday
    December 18th 2016 Week 52nd Sunday
    uva294(唯一分解定理)
    uva11624Fire!(bfs)
    fzu2150Fire Game(双起点bfs)
    poj3276Face The Right Way
  • 原文地址:https://www.cnblogs.com/nanjolno/p/8992732.html
Copyright © 2011-2022 走看看