洛谷P2149 [SDOI2009]Elaxia的路线 spfa+拓扑排序

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/104868506