图论中虚拟原点和反向建图两种方法—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
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 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<

2.反向建边

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

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

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

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

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

废话不多说,上代码

#include
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 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<

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

你可能感兴趣的:(算法,图论)