尋找兩條最短路的公共路徑

Ораичскевеета發表於2024-11-04

尋找兩條最短路的公共路徑

[SDOI2009] Elaxia的路線

題目描述

最近,Elaxia 和 w** 的關係特別好,他們很想整天在一起,但是大學的學習太緊張了,他們必須合理地安排兩個人在一起的時間。

Elaxia 和 w** 每天都要奔波於宿舍和實驗室之間,他們 希望在節約時間的前提下,一起走的時間儘可能的長。

現在已知的是 Elaxia 和 w** 所在的宿舍和實驗室的編號以及學校的地圖:
地圖上有 $n$ 個路口,$m$ 條路,經過每條路都需要一定的時間。 具體地說,就是要求無向圖中,兩對點間最短路的最長公共路徑。

輸入格式

第一行兩個正整數 $n,m$,表示點數和邊數。

第二行四個正整數 $x_1,y_1,x_2,y_2$,分別表示 Elaxia 的宿舍和實驗室及 w** 的宿舍和實驗室的標號。

接下來 $m$ 行,每行三個整數 $u,v,w$,表示 $u,v$之間有一條邊,需要 $w$ 的時間經過。

輸出格式

一行一個整數表示答案。(即最長公共路徑的長度)

樣例 #1

樣例輸入 #1

9 10
1 6 7 8
1 2 1
2 5 2
2 3 3
3 4 2
3 9 5
4 5 3
4 6 4
4 7 2
5 8 1
7 9 1

樣例輸出 #1

3

提示

【資料範圍】
對於 $30%$ 的資料,$1\le n \le 100$;
對於 $60%$ 的資料,$1\le n \le 1000$;
對於 $100%$ 的資料,$1\le n \le 1500$,$1 \leq m \leq 3 \times 10^5$,$1\le w \le 10^4$,輸入資料保證沒有重邊和自環。

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
struct edge{
    int v,nxt;
    int w;
}e1[N*N],e2[N*N];
struct Node{
    int dis,u;
    bool operator<(const Node &a) const{
        return a.dis<dis;
    }
};
int cnt1,cnt2,head1[N],head2[N],ans;
int dis[5][N],in[N],len[N],vis[N];
int n,m,pos1,pos2,pos3,pos4;
void add1(int u,int v,int w){
    e1[cnt1].w=w;
    e1[cnt1].v=v;
    e1[cnt1].nxt=head1[u];
    head1[u]=cnt1++;
}
void add2(int u,int v,int w){
    in[v]++;
    e2[cnt2].w=w;
    e2[cnt2].v=v;
    e2[cnt2].nxt=head2[u];
    head2[u]=cnt2++;
}
void dijkstra(int id,int s){
    priority_queue<Node> pq;
    memset(vis,0,sizeof(vis));
    memset(dis[id],0x3f,sizeof(dis[id]));
    pq.push((Node){0,s});
    dis[id][s]=0;
    while(!pq.empty()){
        int u=pq.top().u;
        int d=pq.top().dis;;
        pq.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head1[u];i+1;i=e1[i].nxt){
            int v=e1[i].v;
            if(vis[v]) continue;
            if(dis[id][v]>d+e1[i].w){
                dis[id][v]=d+e1[i].w;
                pq.push((Node){dis[id][v],v});
            }
        }
    }
}
void tuopu(){
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(!in[i]) q.push(i);
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head2[u];i+1;i=e2[i].nxt){
            int v=e2[i].v;
            len[v]=max(len[v],len[u]+e2[i].w);
            if(--in[v]==0) q.push(v);
        }
    }
}
int main(){
    memset(head1,-1,sizeof(head1));
    scanf("%d%d%d%d%d%d",&n,&m,&pos1,&pos2,&pos3,&pos4);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add1(u,v,w);
        add1(v,u,w);
    }
    dijkstra(1,pos1);dijkstra(2,pos2);
    dijkstra(3,pos3);dijkstra(4,pos4);
    memset(head2,-1,sizeof(head2));
    for(int u=1;u<=n;u++){
        for(int i=head1[u];i+1;i=e1[i].nxt){
            int v=e1[i].v;
            if(dis[1][u]+e1[i].w+dis[2][v]==dis[1][pos2]&&
               dis[3][u]+e1[i].w+dis[4][v]==dis[3][pos4]) add2(u,v,e1[i].w);
        }
    }
    tuopu();
    for(int i=1;i<=n;i++) ans=max(ans,len[i]);
    memset(in,0,sizeof(in));
    memset(len,0,sizeof(len));
    memset(head2,-1,sizeof(head2));
    cnt2=0;
    for(int u=1;u<=n;u++){
        for(int i=head1[u];i+1;i=e1[i].nxt){
            int v=e1[i].v;
            if(dis[1][u]+e1[i].w+dis[2][v]==dis[1][pos2]&&
               dis[4][u]+e1[i].w+dis[3][v]==dis[3][pos4]) add2(u,v,e1[i].w);
        }
    }
    tuopu();
    for(int i=1;i<=n;i++) ans=max(ans,len[i]);
    printf("%d",ans);
    return 0;
}

最關鍵的部分即為這一段

for(int u=1;u<=n;u++){
    for(int i=head1[u];i+1;i=e1[i].nxt){
        int v=e1[i].v;
        if(dis[1][u]+e1[i].w+dis[2][v]==dis[1][pos2]&&
           dis[3][u]+e1[i].w+dis[4][v]==dis[3][pos4]) add2(u,v,e1[i].w);
    }
}

跑過四遍最短路後,即可得到一個點到其他點的距離,因為Elaxia和w**的路線都有多條最短路,然後就可以列舉每條邊,把屬於兩種路線中最短路共有的部分找出來,建一張圖,然後對圖進行拓撲排序,就可得到最長公共路徑。

相關文章