图论中虚拟原点和反向建图两种方法—Acwing1137选择最短路线

虚拟原点和反向建图两种方法(本题中受范围限制运行速度区别不大)(附AC代码)

这是蒟蒻在Acwing的第一篇题解(斗胆求赞)

题目传送门

现在时间是2023/1/26 20:56,给大家拜个

看到题的第一眼就发现了这道题是一道图论中巧妙建图的模板题水题(好在范围也不大,不用加任何的优化)

这道题如果一开始的思路是让某个图论算法跑W遍的话,那大概率会TLE(当然我没试),所以我们不能将这道题的时间复杂度*W

要注意题目中是多组数据!!!每组数据一定要初始化!!!!!!!!

下文附两种本题的主流解法:

1.虚拟原点

通过读题我们发现,我们可以有W个出发车站,难不成我们要将Dijkstra or spfa这种单源最短路算法跑W遍?这显然是不现实的。

所以我们建立一个虚拟原点(出发车站)——“0”。把“0”车站和给定点W个琪琪家附近可以出发的车站连起来,权重为0,这样我们就可以只用跑一遍spfa(本题没有卡spfa)就可以求得到终点站S的最短路径

接下来上SPFA!!!

SPFA的板子就不多说了,关键是每组数据的初始化和判断条件千万不要忘记!

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=200010;
int h[N],ne[M],e[M],w[M],idx,dist[N],n,m,s,st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;//本题的数据规模是稀疏图,邻接表存边
}
queue<int> que;
int spfa()
{
    dist[0]=0;
    que.push(0);
    while(!que.empty())
    {
        auto t=que.front();
        que.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=w[i]+dist[t];
                if(!st[j])
                {
                    que.push(j);
                    st[j]=1;
                }
            }
        }
    }
    if(dist[s]==0x3f3f3f3f)return -1;
    else return dist[s];
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&s)!=EOF)
    {
        idx=0;
        memset(h,-1,sizeof h);
        memset(dist,0x3f,sizeof dist);
        memset(st,false,sizeof st);
        for(int i=1;i<=m;i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            add(a,b,c);
        }
        int tmp;
        cin>>tmp;
        while(tmp--)
        {
            int a;
            scanf("%d",&a);
            add(0,a,0);
        }
        cout<<spfa()<<endl;
    }
    return 0;
}//61行结束————177msAC

2.反向建边

难度和效率与第一个方法相差不多

从许多点到某个点的最短路径集合,其实就是求反向建图的图中,从某点到许多点的最短路径集合,二者是等价的。

反向建图的基本操作其实就是add(a,b,c)变为add(b,a,c)

关于反向建图的详细细节请大家参考blogs(29条消息) 最短路-反向建边(附图详解)_codeducker的博客-CSDN博客

反向建边并且运行完SPFA(一定要记得运行SPFA算法,不然全是0x3f3f3f3f)之后,将给的W个出发点车站一次比较dist,选其中最小的一个座位最终答案输出,如果都是0x3f3f3f3f的话,就只好输出-1啦。

废话不多说,上代码

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=200010;
int h[N],ne[M],e[M],w[M],idx,dist[N],n,m,s,st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
queue<int> que;
void spfa()
{
    dist[s]=0;
    que.push(s);
    st[s]=1;
    while(!que.empty())
    {
        auto t=que.front();
        que.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=w[i]+dist[t];
                if(!st[j])
                {
                    que.push(j);
                    st[j]=1;
                }
            }
        }
    }
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&s)!=EOF)
    {
        idx=0;
        memset(h,-1,sizeof h);
        memset(dist,0x3f,sizeof dist);
        memset(st,false,sizeof st);
        for(int i=1;i<=m;i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            add(b,a,c);
        }
        spfa();//不要忘记运行spfa!!!我因为这个调试了好久
        int tmp;
        cin>>tmp;
        int mm=0x3f3f3f3f;//赋最大值保证安全
        while(tmp--)
        {
            int a;
            scanf("%d",&a);
            mm=min(dist[a],mm);//求出终点到各个起点最短路的最小值即为最终答案
        }
        if(mm==0x3f3f3f3f)puts("-1");//搜不到就没办法了
        else cout<<mm<<endl;
    }
    return 0;
}//63行————184ms,在该题的数据范围下效率和虚拟原点相差不多

如果看完觉得有帮助,请不要吝惜手中的赞赞和收藏哦!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值