寻找两条最短路的公共路径
[SDOI2009] Elaxia的路线
题目描述
最近,Elaxia 和 w** 的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们必须合理地安排两个人在一起的时间。
Elaxia 和 w** 每天都要奔波于宿舍和实验室之间,他们 希望在节约时间的前提下,一起走的时间尽可能的长。
现在已知的是 Elaxia 和 w** 所在的宿舍和实验室的编号以及学校的地图:
地图上有
n
n
n 个路口,
m
m
m 条路,经过每条路都需要一定的时间。 具体地说,就是要求无向图中,两对点间最短路的最长公共路径。
输入格式
第一行两个正整数 n , m n,m n,m,表示点数和边数。
第二行四个正整数 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 x1,y1,x2,y2,分别表示 Elaxia 的宿舍和实验室及 w** 的宿舍和实验室的标号。
接下来 m m m 行,每行三个整数 u , v , w u,v,w u,v,w,表示 u , v u,v u,v之间有一条边,需要 w w 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
%
30\%
30% 的数据,
1
≤
n
≤
100
1\le n \le 100
1≤n≤100;
对于
60
%
60\%
60% 的数据,
1
≤
n
≤
1000
1\le n \le 1000
1≤n≤1000;
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
1500
1\le n \le 1500
1≤n≤1500,
1
≤
m
≤
3
×
1
0
5
1 \leq m \leq 3 \times 10^5
1≤m≤3×105,
1
≤
w
≤
1
0
4
1\le w \le 10^4
1≤w≤104,输入数据保证没有重边和自环。
#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**的路线都有多条最短路,然后就可以枚举每条边,把属于两种路线中最短路共有的部分找出来,建一张图,然后对图进行拓扑排序,就可得到最长公共路径。