luogu P1073 最优贸易

来源:互联网 发布:大平面铣削编程 编辑:程序博客网 时间:2024/05/17 23:50

题目描述

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。

NOIP 2009 提高组 第三题


题目很长,总结一下,就是说我们有一张图,图中有一些有向边,有一些无向边,然后每个点有点权,要求我们找一条由1到n的路径,使得这条路径上的点A和点B的点权差值最大,且要求保证节点A在节点B之前遍历到,且节点B的点权大于节点A的点权;


乍一看没什么思路,但是仔细分析,发现我们要找一条1到n的路径上的两个最值点,而且要使点权大的点后遍历到,那么,第一个想法就是搜索;

搜索: 首先,我们从1开始遍历的时候要更新最小值和最大值,又因为有先后这个条件,我们还需要记录遍历到的时间,然后,再次遍历去处理,但是显然这样子极难实现,就算实现了,也会有很多bug,就算没有bug,也肯定会TLE,所以,我们要另寻他路;


还是根据遍历要求先后这个条件,我们可以根据显然定理,经分析发现,点权小的那个点,一定是靠近1的,而同理,点权大的点靠近n;

由以上分析可得,这个题我们需要正反两次搜索,一次求最大值,一次求最小值,而且由于是搜索,而且没有太多限制(遍历先后这个限制我们拆成了两个搜索,等价于没有了这个限制),所以我们可以选择用spfa来解决这道题;


正解:

我们从n开始,做一次spfa,求到达每个点的最大值,并且标记达到过哪些点,这样我们就可以知道那些点可以到达n,然后从1开始,做一次spfa,更新最小值时,如果这个点可以达到n,我们就更新答案ans;做完两次spfa后,输出ans即可;


至于代码:

#include<iostream>#include<cstdio>#include<queue>#define II int#define R register#define I 1000010using namespace std;struct node {    II to,up;}aa[I],aa_f[I];queue <II> Q;II inq[I],head[I],head_f[I],out_f[I],out[I],boby[I],mi[I],ma[I],has[I];II n,m,_tot,_tot_f,ans;void add(R II x,R II y){    out[x]++;    aa[++_tot].to=y;    aa[_tot].up=head[x];    head[x]=_tot;    //正着存图;    out_f[y]++;    aa_f[++_tot_f].to=x;    aa_f[_tot_f].up=head_f[y];    head_f[y]=_tot_f;    //倒着再存一遍图;}void spfa(){    Q.push(n);    inq[n]=1;    while (!Q.empty()) {        R II x=Q.front();        Q.pop(); inq[x]=0;        for(R II i=head_f[x];i;i=aa_f[i].up)        {            R II go=aa_f[i].to;            if(out_f[x]&&!inq[go]) inq[go]=1,Q.push(go),out_f[x]--,has[x]=1;            //每个点所连的边遍历一次就好;            if(ma[go]<ma[x]&&out_f[x]) ma[go]=ma[x];            //更新最大值;        }    }}void spfa_1(){    for(R II i=1;i<=n;i++) inq[i]=0;    Q.push(1);    inq[1]=1;    while (!Q.empty()) {        R II x=Q.front();        Q.pop(); inq[x]=0;        for(R II i=head[x];i;i=aa[i].up)        {            R II go=aa[i].to;            if(mi[go]>mi[x]) mi[go]=mi[x];            //更新最小值;            if(out[x]&&!inq[go]) inq[go]=1,Q.push(go),out[x]--;            //同理,每个点连的边只需要走一次就好;            if(has[go]) ans=max(ans,ma[go]-mi[go]);            //如果这个点可以到达n,更新答案;        }    }    printf("%d\n",ans);}int main(){    scanf("%d%d",&n,&m);    for(R II i=1;i<=n;i++) scanf("%d",&boby[i]),mi[i]=boby[i],ma[i]=boby[i];    //最开始每个点的最大值、最小值都是自己;    for(R II i=1;i<=m;i++)    {        R II x,y,z;        scanf("%d%d%d",&x,&y,&z);        if(z==1) add(x,y);        //单向边;          else add(x,y),add(y,x);          //双向边;    }    spfa();    //倒着从n做spfa;    spfa_1();    //正着从1做spfa;    return 0;}

by pretend-fal;

END;

原创粉丝点击