【题解】P7669 [JOI 2018 Final] 月票购买 Commuter Pass
P7669 [JOI 2018 Final] 月票购买 / Commuter Pass
题意
给定一张 \(n\) 个点 \(m\) 条边的无向连通正权图,再给出四个点 \(S,T,U,V\),请仅选择一条从 \(S\) 到 \(T\) 的最短路,并将路径上每条边的边权改为 \(0\),最后求出所有选择方案中,修改之后从 \(U\) 到 \(V\) 的最短路长度的最小值。
题解
知识点:最短路,动态规划。
比较平凡的题目。
先分别处理出 \(U,V,S\) 的单源最短路数组 \(dst,ded,ds\),并赋值答案上界为 \(dst_V\),考虑的是 \(U\) (可能)不经过 \(S\) 到 \(T\) 的最短路到达 \(V\) 的情况。
接着考虑如果经过 \(S\) 到 \(T\) 最短路时的情况,设 \(i,j\) 均为 \(S\) 到 \(T\) 的同一条最短路上的点,则答案为 \(\displaystyle \min_{i,j\in path_{S,T}} (dst_i+ded_j)\)。
取出 \(S\) 到 \(T\) 其中的一条最短路 \(path_{S,T}\) 考虑,设 \(dp_i\) 为从 \(S\) 出发处理到点 \(i\) 时从 \(U\) 到 \(V\) 且过点 \(i\) 的最短路,则有 \(\displaystyle dp_i=\min(dst_i+\min_{j\in path_{S,i}} ded_j,ded_i+\min_{j\in path_{S,i}} dst_j)\),答案为 \(\displaystyle \min_{i\in path_{S,T}} dp_i\)。
考虑限制,\(i,j\) 只能在 \(S\) 到 \(T\) 的同一条最短路上,不过暴力枚举每一条最短路显然是错误的,通过简单构造可以将复杂度卡到指数级。
所以要考虑把所有可能的最短路一起处理掉。
具体地,建一个新的无权图 \(G\),初始时都是孤点,对于原图上满足 \(ds_u+w=ds_v\) 的边 \((u,v,w)\) 在 \(G\) 上连一条从 \(v\) 指向 \(u\) 的有向边,由于边权是正权,可以发现新图一定是一个 DAG,且汇点为 \(S\),源点为 \(T\)。
还是设 \(dp_i\) 为从 \(S\) 出发走到 \(i\) 时从 \(U\) 到 \(V\) 且过点 \(i\) 的最短路,\(dps_i\) 为 \(i\) 之前的点中 \(dst\) 的最小值,\(dpe_i\) 为 \(i\) 之前的点中 \(ded\) 的最小值,有如下转移:
答案即为 \(\displaystyle \max_{i\in G} dp_i\),转移可以用拓扑排序或者记忆化搜索实现。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define int long long
#define N 202507
int n,m,ss,tt,st,ed,ans=2e18;
int dst[N],ded[N],ds[N];
int dps[N],dpe[N];
struct edge{
int t,w;
};
vector<edge>e[N];
bitset<N>f;
inline void dijkstra(int s,int *dis){
rep(i,1,n){
dis[i]=1e18;
f[i]=0;
}
dis[s]=0;
priority_queue<pr,vector<pr>,greater<pr>>q;
q.push({dis[s],s});
while(!q.empty()){
pr u=q.top();
q.pop();
if(f[u.se]){
continue;
}
f[u.se]=1;
for(edge x:e[u.se]){
if(dis[u.se]+x.w<dis[x.t]){
dis[x.t]=dis[u.se]+x.w;
q.push({dis[x.t],x.t});
}
}
}
}
inline void sol(int k){
if(f[k]){
return;
}
f[k]=1;
dps[k]=dst[k];
dpe[k]=ded[k];
for(edge x:e[k]){
if(ds[x.t]+x.w!=ds[k]){
continue;
}
sol(x.t);
ans=min({ans,dps[x.t]+ded[k],dpe[x.t]+dst[k]});
dps[k]=min(dps[k],dps[x.t]);
dpe[k]=min(dpe[k],dpe[x.t]);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
cin>>ss>>tt>>st>>ed;
rep(i,1,m){
int u,v,w;
cin>>u>>v>>w;
e[u].pb({v,w});
e[v].pb({u,w});
}
dijkstra(st,dst);
dijkstra(ed,ded);
ans=dst[ed];
dijkstra(ss,ds);
rep(i,1,n){
dps[i]=2e18;
dpe[i]=2e18;
f[i]=0;
}
sol(tt);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号