[南海云课堂] [DP 与最短路的关系] [最短路] [分层图] 道路与航线
posted on 2023-08-09 10:58:31 | under 题集 | source
题意
C 国有 \(n\) 个大城市和 \(m\) 条道路,每条道路连接这 \(n\) 个城市中的某两个城市。
任意两个城市之间最多只有一条道路直接相连。
这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 \(1\) 条。
C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。
但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到 C 国旅游。
当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。
设 C 国 \(n\) 个城市的标号从 \(1∼n\),阿龙决定从 \(1\) 号城市出发,并最终在 \(n\) 号城市结束自己的旅行。
在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 \(n\) 个城市。
阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。
因为阿龙主要是来 C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
现在给出 \(n\) 个城市的水晶球价格,\(m\) 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。
请你告诉阿龙,他最多能赚取多少旅费。
思路
-
最短路
一次贸易分为买入和卖出,考虑分两部分处理。
枚举 \(i\),求出阿龙在 \(1\) 走向 \(i\) 时最少花 \(a_i\) 元买入,在 \(i\) 走向 \(n\) 时最多卖出 \(b_i\) 元,求 \(\max\limits_{1≤i≤n}{b_i-a_i}\) 即可。
容易想到 \(\rm {DP}\) 求 \(a\)、\(b\),但图中可能有环,因此 \(\rm{DP}\) 存在后效性。
在部分情况下最短路算法可替代 \(\rm{DP}\),因此当 \(\rm{DP}\) 不行时,我们考虑最短路算法。
同上,\(\rm{Dijkstra}\) 也存在后效性,只能使用 \(\rm{SPFA}\) 处理了。
建反边跑两次 \(SPFA\),这道题就解决了。
-
分层图
这道题和 T3 优惠 的分层图思想差不多。
定义 \((u,0/1,0/1)\) 代表到达城市 \(u\) 时,是否持有水晶球,是否进行过交易。
建边分为两类,单纯传递信息和联系不同状态:
- 传递信息:
- \((u,0,0)\) \(->\) \((v,0,0)\),边权为 \(0\)。
- \((u,1,0)\) \(->\) \((v,1,0)\),边权为 \(0\)。
- \((u,0,1)\) \(->\) \((v,0,1)\),边权为 \(0\)。
- 联系状态:
- \((u,0,0)\) \(->\) \((u,1,0)\),边权为 \(-w_u\),表示花费 \(w_u\) 买入一个水晶球。
- \((u,1,0)\) \(->\) \((u,0,1)\),边权为 \(w_u\),表示卖出水晶球并获得 \(w_u\) 元。
然后求出 \((1,0,0)\) 到 \((n,0,1)\) 的最短路即可。
代码
-
最短路
此题给出的是点权而不是边权,需要将点权赋给相连的边。于是,对于 \(e(u,v)\),令 \(w(u,v)=w_v\)。
之所以不往前赋值,是因为往前赋值会忽略掉终点(和起点)的点权,处理有些麻烦。而往后赋值只需预处理源点就好了。
#include<bits/stdc++.h> using namespace std; const int N=1e5+5,M=5e5+5,INF=0x3f3f3f3f; int n,m,u,v,f,w[N],head[N],head2[N],cnt,cnt2; int dis[N],vis[N],dis2[N],vis2[N],ans; struct edge{ int v,nxt,w; }e[2*M],e2[2*M]; void add(int u,int v,int w){ e[++cnt]={v,head[u],w}; head[u]=cnt; } void add2(int u,int v,int w){ e2[++cnt2]={v,head2[u],w}; head2[u]=cnt2; } void insert(int u,int v){ add(u,v,w[u]); add2(v,u,w[v]); } void SPFA(int k){ for(int i=1; i<=n;i++){ dis[i]=114514; } queue<int>q; q.push(k); vis[k]=1; dis[k]=w[k]; while(!q.empty()) { k=q.front(); q.pop(); vis[k]=0; for(int i=head[k]; i;i=e[i].nxt) { int j=e[i].v; if(min(dis[k],e[i].w)<dis[j]) { dis[j]=min(dis[k],e[i].w); if(vis[j]==0) { q.push(j); vis[j]=1; } } } } } void SPFA2(int k){ for(int i=1; i<=n;i++){ dis2[i]=-114514; } queue<int>q2; q2.push(k); vis2[k]=1; dis2[k]=w[k]; while(!q2.empty()) { k=q2.front(); q2.pop(); vis2[k]=0; for(int i=head2[k]; i;i=e2[i].nxt) { int j=e2[i].v; if(max(dis2[k],e2[i].w)>dis2[j]) { dis2[j]=max(dis2[k],e2[i].w); if(vis2[j]==0) { q2.push(j); vis2[j]=1; } } } } } int main(){ cin>>n>>m; for(int i=1; i<=n;i++){ scanf("%d",&w[i]); } for(int i=1; i<=m;i++){ scanf("%d%d%d",&u,&v,&f); if(f==2){ insert(u,v); insert(v,u); } else{ insert(u,v); } } SPFA(1); SPFA2(n); for(int i=1; i<=n;i++){ ans=max(ans,dis2[i]-dis[i]); } cout<<ans; return 0; } -
分层图
分层图真是太强啦!
#include<bits/stdc++.h> using namespace std; const int N=4e5+5,M=4*5e5+5; int n,m,u,v,f,head[N],vis[N],cnt; int w[N],dis[N]; queue<int>q; struct edge{ int v,nxt,w; }e[2*M]; void add(int u,int v,int w){ e[++cnt].v=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt; } inline int id(int u,int p,int pp){ return (pp+p*2)*n+u; } void insert(int u,int v){ add(id(u,0,0),id(v,0,0),0); add(id(u,1,0),id(v,1,0),0); add(id(u,0,1),id(v,0,1),0); } void SPFA(int k){ memset(dis,-0x3f,sizeof(dis)); q.push(k); vis[k]=1; dis[k]=0; while(!q.empty()) { k=q.front(); q.pop(); vis[k]=0; for(int i=head[k]; i;i=e[i].nxt) { int j=e[i].v; if(dis[k]+e[i].w>dis[j]) { dis[j]=dis[k]+e[i].w; if(vis[j]==0) { q.push(j); vis[j]=1; } } } } } int main(){ cin>>n>>m; for(int i=1; i<=n;i++){ scanf("%d",&w[i]); add(id(i,0,0),id(i,1,0),-w[i]); add(id(i,1,0),id(i,0,1),w[i]); } for(int i=1; i<=m;i++){ scanf("%d%d%d",&u,&v,&f); if(f==1){ insert(u,v); } else{ insert(u,v); insert(v,u); } } SPFA(id(1,0,0)); if(dis[id(n,0,1)]<0){ cout<<0; } else{ cout<<dis[id(n,0,1)]; } return 0; }

浙公网安备 33010602011771号