D97 最短路径图+拓扑排序 Dijkstra 算法 P2149 [SDOI2009] Elaxia的路线

D97 最短路径图+拓扑排序 Dijkstra 算法 P2149 [SDOI2009] Elaxia的路线_哔哩哔哩_bilibili

 

P2149 [SDOI2009] Elaxia的路线 - 洛谷

给一个无向图(没有重边和自环)和两对点,求两对点之间的最短路的最长公共路径的长度。

图中,1 到 6 的最短路有两条,7 到 8 的最短路有一条,最长公共路径的长度为 3

image

思路

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

image

甲乙两人的最短路图各自构成一个 DAG,在两个 DAG 上找出最长公共链

公共链可能是同向走的最长链,也可能是反向走的最长链,不可能同向和反向同时取到

图中,两人的 DAG 图有四条相交的公共链,答案只能是四条中的最长链

image

先预处理以 4 个点为起点的最短路

然后,标记甲的 DAG 图上的边和点的入度

然后,对甲的 DAG 图做拓扑排序,如果 (u,v) 也是乙的 DAG 中的边,那么累计长度。同向走累计到 f 数组,反向走累计到 g 数组,取其最大

相关板子:

D92【模板】最短路径树 Dijkstra 算法 CF545E Paths and Trees - 董晓 - 博客园

D01【模板】拓扑排序 - 董晓 - 博客园

 

// 最短路图+拓扑排序 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);
}

 

posted @ 2026-03-05 20:35  董晓  阅读(27)  评论(0)    收藏  举报