D97 最短路径图+拓扑排序 Dijkstra 算法 P2149 [SDOI2009] Elaxia的路线
D97 最短路径图+拓扑排序 Dijkstra 算法 P2149 [SDOI2009] Elaxia的路线_哔哩哔哩_bilibili
P2149 [SDOI2009] Elaxia的路线 - 洛谷
给一个无向图(没有重边和自环)和两对点,求两对点之间的最短路的最长公共路径的长度。
图中,1 到 6 的最短路有两条,7 到 8 的最短路有一条,最长公共路径的长度为 3

思路
观察推断,所求最长公共路径是一条链,因为最短路的一部分还是最短路,同时取两段链一定矛盾

甲乙两人的最短路图各自构成一个 DAG,在两个 DAG 上找出最长公共链
公共链可能是同向走的最长链,也可能是反向走的最长链,不可能同向和反向同时取到
图中,两人的 DAG 图有四条相交的公共链,答案只能是四条中的最长链

先预处理以 4 个点为起点的最短路
然后,标记甲的 DAG 图上的边和点的入度
然后,对甲的 DAG 图做拓扑排序,如果 (u,v) 也是乙的 DAG 中的边,那么累计长度。同向走累计到 f 数组,反向走累计到 g 数组,取其最大
相关板子:
D92【模板】最短路径树 Dijkstra 算法 CF545E Paths and Trees - 董晓 - 博客园
// 最短路图+拓扑排序 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define pii pair<int,int> using namespace std; const int N=1505,M=6e5+5; int idx,h[N],to[M],ww[M],ne[M]; void add(int u,int v,int w){ to[++idx]=v,ww[idx]=w,ne[idx]=h[u],h[u]=idx; } int n,m,s1,t1,s2,t2; int d[4][N]; void dijkstra(int s,int k){ memset(d[k],0x3f,sizeof d[k]); d[k][s]=0; priority_queue<pii,vector<pii>,greater<pii> > q; q.emplace(0,s); while(!q.empty()){ auto [dd,u]=q.top(); q.pop(); if(dd!=d[k][u]) continue; for(int i=h[u];i;i=ne[i]){ int v=to[i],w=ww[i]; if(d[k][v]>d[k][u]+w){ d[k][v]=d[k][u]+w; q.emplace(d[k][v],v); } } } } int on[M],rd[N],f[N],g[N],ans; void topo(){ queue<int> q; q.push(s1); //对甲的DAG图做拓扑排序 while(!q.empty()){ int u=q.front(); q.pop(); ans=max({ans,f[u],g[u]}); for(int i=h[u]; i; i=ne[i])if(on[i]){ //如果边i是甲的DAG中的边 int v=to[i],w=ww[i]; //如果(u,v)也是乙的DAG中的边,那么累计长度 if(d[2][u]+w+d[3][v]==d[2][t2]) f[v]=max(f[v],f[u]+w); //同向走 if(d[3][u]+w+d[2][v]==d[2][t2]) g[v]=max(g[v],g[u]+w); //反向走 if(--rd[v]==0) q.push(v); } } } int main(){ scanf("%d%d%d%d%d%d",&n,&m,&s1,&t1,&s2,&t2); for(int i=1,u,v,w;i<=m;i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w); dijkstra(s1,0),dijkstra(t1,1); dijkstra(s2,2),dijkstra(t2,3); //预处理以4个点为起点的最短路 for(int u=1;u<=n;u++)for(int i=h[u];i;i=ne[i]){ int v=to[i],w=ww[i]; if(d[0][u]+w+d[1][v]==d[0][t1]) on[i]=1,rd[v]++; //标记甲的DAG图上的边和点的入度 } topo(); printf("%d",ans); }
浙公网安备 33010602011771号