题目链接:https://www.luogu.com.cn/problem/P2149
题意很简单,找两个最短路的最长公共路径。因为两个点之间有多条最短路的路径,所以我们必须要把所有最短路的路径全部找出来并作上标记。样例的图如下(略丑~)
从图上,我们可以清晰地看到,从 v1-v6 有两条最短路径,分别是 v1-v2-v3-v4-v6 与 v1-v2-v5-v4-v6。但从 v7-v8 只有一条最短路径,即 v7-v4-v5-v8。所以,明显我们 v1-v6 选择第二种方案更好,这样就有一条长为 3 的公共边 v4-v5。
所以我们需要将 A(第一个人)的所有最短路径找出来,在图上打上标记,或者重新建图(博主这里是打标记)。这里有一个问题,就是怎么把两个点的多条最短路径找出来? 一般我们都是找任意一条最短路径即可,现在要都找出来,怎么办呢。这里有一个很巧妙的方法,假如现在是找 s1-t1的最短路径,我们首先用spfa找源点为 s1 的最短路 dis1[],之后再用spfa找源点为 t1 的最短路dis2[],记 Mi=s1-t1 的最短路长度,我们遍历所有边,只要 dis1[这条边的起点]+dis2[这条边的终点]+这条边的长度 = Mi,那么这条边就是 s1-t1 的最短路径上的一条边。
我们找完 A 的 s1-t1 的最短路径,这是DAG(有向无环图),我们只需要再找 B 的 源点为 s2 的最短路,源点为 t2 的最短路,然后遍历所有边,是 s2-t2 最短路上的边就打上标记(方法和上面一样),注意是无向边,来回都要打上标记。
最后利用拓扑排序dp, 如果改变不是 s1-t1 最短路径上的边,直接continue,如果是,只有同时使 s2-t2 最短路径上的边,才能加上这条边的长度与更新dp,最后dp[t1]就是答案。
代码如下
#include <bits/stdc++.h>
using namespace std;
const int maxn=1505;
const int inf=0x7fffffff;
struct node
{
int from,to,next,value;
}edge[maxn*maxn];
int cnt=1;
int head[maxn];
int dis1[maxn],dis2[maxn];
int top,now;
int que[maxn*maxn];//队列
bool vis[maxn];//标记是否在队列中
bool vis1[maxn*maxn];//标记是否在A的最短路中
bool vis2[maxn*maxn];//标记是否在B的最短路中
int n,m,s1,t1,s2,t2;
int dp[maxn];
int indeg[maxn];
void add(int x,int y,int w)
{
edge[++cnt].to=y;
edge[cnt].from=x;
edge[cnt].next=head[x];
edge[cnt].value=w;
head[x]=cnt;
}
void spfa(int dis[],int f)
{
for(int i=1;i<=n;i++)
dis[i]=inf,vis[i]=false;
dis[f]=0;
vis[f]=true;
top=now=0;
que[top++]=f;
while(now<top)
{
int u=que[now++];
//cout<<u<<endl;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
//cout<<v<<endl;
int w=edge[i].value;
//cout<<w<<endl;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
que[top++]=v,vis[v]=true;
}
}
vis[u]=false;
}
}
int main()
{
scanf("%d %d",&n,&m);
scanf("%d %d %d %d",&s1,&t1,&s2,&t2);
for(int i=1;i<=m;i++)
{
int x,y,w;
scanf("%d %d %d",&x,&y,&w);
add(x,y,w);
add(y,x,w);
}
spfa(dis1,s1);
spfa(dis2,t1);
int Mi=dis1[t1];
//for(int i=1;i<=n;i++)
//cout<<dis1[i]<<endl;
for(int i=2;i<=cnt;i++)
if(dis1[edge[i].from]+dis2[edge[i].to]+edge[i].value==Mi)
vis1[i]=true,indeg[edge[i].to]++;
spfa(dis1,s2);
spfa(dis2,t2);
Mi=dis1[t2];
for(int i=2;i<=cnt;i++)
if(dis1[edge[i].from]+dis2[edge[i].to]+edge[i].value==Mi)
vis2[i]=true,vis2[i^1]=true;
top=now=0;
for(int i=1;i<=n;i++)
if(!indeg[i])
que[top++]=i;
while(now<top)
{
int u=que[now++];
for(int i=head[u];i;i=edge[i].next)
{
if(!vis1[i])
continue;
int v=edge[i].to;
int w=edge[i].value;
indeg[v]--;
if(!indeg[v])
que[top++]=v;
dp[v]=max(dp[v],vis2[i]?dp[u]+w:dp[u]);
}
}
printf("%d\n",dp[t1]);
return 0;
}