P1073 [NOIP2009 提高组] 最优贸易 (最短路/分层图)
[NOIP2009 提高组] 最优贸易
题目描述
$C n m$ 条道路,每条道路连接这 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 条。
国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 国 n 个城市的标号从 ,阿龙决定从 号城市出发,并最终在 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
假设 国有 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。
假设 号城市的水晶球价格分别为 。
阿龙可以选择如下一条线路:->->->,并在 $2 3$ 的价格买入水晶球,在 号城市以的价格卖出水晶球,赚取的旅费数为 2。
阿龙也可以选择如下一条线路->->->->,并在第$1 51 $的价格买入水晶球,在第 次到达 号城市时以 的价格卖出水晶球,赚取的旅费数为。
现在给出 $n m$ 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。
输入格式
第一行包含 个正整数和 ,中间用一个空格隔开,分别表示城市的数目和道路的数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
接下来 行,每行有$ 3 x,y,z$,每两个整数之间用一个空格隔开。如果 ,表示这条道路是城市$ x y z=2x y $之间的双向道路。
输出格式
一 个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出 。
样例 #1
样例输入 #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
提示
【数据范围】
输入数据保证 号城市可以到达号城市。
对于 10%的数据,。
对于 30%的数据,。
对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。
对于 100%的数据,,,,,,各城市
水晶球价格。
NOIP 2009 提高组 第三题
代码
常规最短路解法
// Problem: 最优贸易 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/343/ // Memory Limit: 64 MB // Time Limit: 1000 ms // Created Time: 2022-07-12 14:59:10 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define pll pair <ll, ll> #define endl '\n' #define il inline #define pb push_back #define fi first #define se second #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=1e5+10,M=2e6+5; int hs[N],ht[N],e[M],ne[M],w[M],idx; int n,m; int dmin[N],dmax[N]; bool st[N]; int q[N]; void spfa(int h[],int dist[],int type)//因为可能会重复更新所以不能用dijkstra { int hh=0,tt=1; if(type==0)//求最小值 { memset(dist,0x3f,sizeof dmin); dist[1]=w[1]; q[0]=1; } else//求最大值 { memset(dist,-0x3f,sizeof dmax); dist[n]=w[n]; q[0]=n; } while(hh!=tt) { int t=q[hh++]; if(hh==N)hh=0; st[t]=false; for(int i=h[t];~i;i=ne[i]) { int j=e[i]; if((type==0&&dist[j]>min(dist[t],w[j]))||(type==1&&dist[j]<max(dist[t],w[j]))) { if(type==0)dist[j]=min(dist[t],w[j]); else dist[j]=max(dist[t],w[j]); if(!st[j]) { q[tt++]=j; if(tt==N)tt=0; st[j]=true; } } } } } void add(int h[],int a,int b) { e[idx]=b; ne[idx]=h[a]; h[a]=idx++; } int main() { cin>>n>>m; for(int i=1;i<=n;i++)cin>>w[i]; memset(hs,-1,sizeof hs);//正向图,1-i求最小值 memset(ht,-1,sizeof ht);//反向图 求i-n最大值,建反图是为了以n为起点走到i while(m--) { int a,b,t; cin>>a>>b>>t; add(hs,a,b);add(ht,b,a); if(t==2) { add(hs,b,a);add(ht,a,b); } } spfa(hs,dmin,0);//求最小值 spfa(ht,dmax,1);//求最大值 int res=0; for(int i=1;i<=n;i++)res=max(res,dmax[i]-dmin[i]);//枚举买入和卖出的分界点 cout<<res<<endl; return 0; }
分层图解法 参考自AcWing 341. 最优贸易 分层图解法+SPFA
// Problem: P1073 [NOIP2009 提高组] 最优贸易 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P1073 // Memory Limit: 125 MB // Time Limit: 1000 ms // Created Time: 2022-07-12 15:51:43 // // Powered by CP Editor (https://cpeditor.org) //fw #include<bits/stdc++.h> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define pll pair <ll, ll> #define endl '\n' #define il inline #define pb push_back #define fi first #define se second #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=3e5+10,M=6e6+5; int h[N],e[M],ne[M],w[M],idx; int dist[N]; bool st[N]; int n,m; void add(int a,int b,int c=0) { e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++; } void spfa() { memset(dist,-0x3f,sizeof dist); queue<int>q; q.push(1); st[1]=true; dist[1]=0; while(q.size()) { int t=q.front(); q.pop(); st[t]=false; for(int i=h[t];~i;i=ne[i]) { int j=e[i]; if(dist[j]<dist[t]+w[i]) { dist[j]=dist[t]+w[i]; if(!st[j]) { st[j]=true; q.push(j); } } } } } int main() { memset(h,-1,sizeof h); cin>>n>>m; for(int i=1;i<=n;i++) { int c; cin>>c; add(i,i+n,-c);//建立买入边 add(i+n,i+2*n,c);//建立卖出边 } for(int i=1;i<=m;i++) { int x,y,z; cin>>x>>y>>z; add(x,y),add(x+n,y+n),add(x+2*n,y+2*n);//在同一层图内边权为0 if(z==2) { add(y,x);add(y+n,x+n),add(y+2*n,x+2*n); } } spfa();//求最长路即可 cout<<dist[3*n]; return 0; }
本文作者:Avarice_Zhao
本文链接:https://www.cnblogs.com/avarice/p/16470342.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步