• 图论2-次短路计数


    上一篇博客我稍微讲了一下k短路,这次,我来讲一下最短路以及次短路计数。要求:能熟练打出普通最短路。

    还是用一道题来引入这个知识点:POJ3463 Sightseeing

    题目大意:给定一个有向图,求最短路以及最短路长度加1的路径条数。

    这道题让求最短路计数也就罢了,但是我们还要对最短路+1的路径计数,这可怎么办呢?计数让我们联想到了

    dp,但是如果按照往常在dp的话,这张图必须是DAG,但没有任何地方说了这个图是DAG,并且因为有权值,

    我们不能够进行缩点,所以DAG上dp的这条路就被封死了。

    所以该怎么办,我们可以在dijkstra或SPFA上直接dp吗?答案是可以,因为每次都会由一个点扩展下一个点,

    这样就可以做到在两个点之间dp转移,但是要如何转移呢?如果是仅仅最短路计数的话就会十分容易,可以

    用一个dp[i]来记录s到i的路径长度为dis[i]时的路径条数。如果最短路被更新了,直接dp[i]=dp[la]即可,没

    有更新且这条路径与最短路相等的话,将dp[i]加等于dp[la]即可。但是如果将这个扩展到次短路该怎么办呢?

    就用两个数组即可,但为了方便,我们用dis[i][0/1]和dp[i][0/1]分别表示从s到i点的 最短路/次短路 长度和从

    s到i点的 最短路/次短路 路径条数。

    而转移就是这样的:

    1.如果小于最短路:原先最短路变为次短路,次短路条数变为原先最短路条数,然后更新最短路及其条数。

    2.如果等于最短路:直接累计最短路条数即可。

    3.如果小于次短路且大于最短路:直接更新次短路及其条数

    4.如果等于次短路:累计次短路条数。

    注意:

    1.我接下来要展示的代码用了万能头,但poj里理论上是不让用万能头的

    2.算完了以后不要忘记判断最短路长度是否是次短路-1

    3.一定不要忘记这道题是多组数据,而且要记得初始化

    4.这是一个有向图不是无向图

    5.一定要记得排除重复,也就是代码中的vis的作用。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int NR=1e3+5;
    const int MR=2e4+5;
    int read()
    {
        int x=0,f=1,c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    struct Edge//邻接表结构体 
    {
        int to,next,w;
    }e[MR];
    int head[NR],cnt;
    void add(int a,int b,int c)//邻接表存图 
    {
        cnt++;
        e[cnt].to=b;
        e[cnt].w=c;
        e[cnt].next=head[a];
        head[a]=cnt;
    }
    int T,m,n;
    int dis[NR][2];
    int dp[NR][2];
    bool vis[NR][2];
    struct Nd//dijkstra必须的结构体 
    {
        int dis,id,num;
        bool operator <(const Nd A) const//优先队列以什么优先 
        {
            return dis>A.dis;//注意一定要是>号因为priority_queue默认是大根堆 
        }
    };
    void Init()//多组数据不要忘记Init 
    {
        memset(head,0,sizeof(head));
        cnt=1;
    }
    void dij(int s,int t)//dijkstra要记录起终点 
    {
        priority_queue <Nd> q; //优先队列 
        memset(dis,0x3f,sizeof(dis));//初始化极大值 
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        Nd tmp;tmp.dis=0,tmp.id=s,tmp.num=0;
        q.push(tmp);dis[s][0]=0,dp[s][0]=1;//赋初值 
        while(!q.empty())
        {
            int x=q.top().id,num=q.top().num;q.pop();//将dis最小的取出,并记录当前是最短路还是次短路 
            if(vis[x][num]) continue;//如果这个点这个的最短/次短遍历过,直接跳过 
            vis[x][num]=1;//vis记录信息 
            for(int i=head[x];i;i=e[i].next)//枚举下一个点 
            {
                int y=e[i].to,cur=dis[x][num]+e[i].w;//记录下一个点,最短路长度。 
                if(cur<dis[y][0])//第一种情况 
                {
                    dis[y][1]=dis[y][0]; 
                    dp[y][1]=dp[y][0];//原先的最短路变为次短路 
                    dis[y][0]=cur;
                    dp[y][0]=dp[x][num];//更新最短路长度及其条数 
                    tmp.dis=dis[y][0],tmp.id=y,tmp.num=0;q.push(tmp);//将这个点这种状态push进优先队列
                    tmp.dis=dis[y][1],tmp.id=y,tmp.num=1;q.push(tmp);//将这个点这种状态push进优先队列
                    continue;
                }
                if(cur==dis[y][0]) //第二种情况 
                { 
                    dp[y][0]+=dp[x][num];//累计最短路条数 
                    continue;
                }
                if(cur<dis[y][1]) //第三种情况 
                { 
                    dis[y][1]=cur;
                    dp[y][1]=dp[x][num]; //更新次短路 
                    tmp.dis=dis[y][1],tmp.id=y,tmp.num=1;q.push(tmp); //将这个点这种状态push进优先队列
                    continue;
                }
                if(cur==dis[y][1]) //第四种情况 
                { 
                    dp[y][1]+=dp[x][num];//累计次短路条数 
                    continue;
                }
            }
        }
    }
    int main()
    {
        T=read();//多组数据的数量 
        while(T--)
        {
            Init();
            n=read(),m=read();
            for(int i=1;i<=m;i++)
            {
                int x=read(),y=read(),z=read();
                add(x,y,z); //有向图建图 
            }
            int s=read(),t=read();
            dij(s,t);
            int ans=dp[t][0];
            if(dis[t][1]-1==dis[t][0]) ans+=dp[t][1];//如果次短路是最短路+1,ans累计上dp[t][1] 
            printf("%d
    ",ans); 
        }
        return 0;
    }
  • 相关阅读:
    402STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)微信小程序扫码绑定Air302并通过阿里云物联网平台实现远程通信控制
    002CH32V307(WCH单片机)学习开发CH32V307VCT6开发板硬件使用说明,下载和运行第一个程序
    200CH582M学习开发蓝牙键盘例程测试
    401STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)Android使用APUConfig配网绑定ESP8266并通过阿里云物联网平台实现远程通信控制
    840Air724UG模块(4G全网通GPRS开发)摄像头默认设置是8W像素, 需要把像素调整到30W看此篇
    003CH32V307(WCH单片机)学习开发官方资料学习说明,开发板串口, USB, 网口(LAN8720, 自带PHY), SDIO(SD卡)通信测试
    401STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)微信小程序使用APUConfig配网绑定ESP8266并通过阿里云物联网平台实现远程通信控制
    402STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)APP扫码绑定Air302并通过阿里云物联网平台实现远程通信控制
    Golang中常用的代码优化点
    golang程序优化方法
  • 原文地址:https://www.cnblogs.com/chen-1/p/12558497.html
Copyright © 2020-2023  润新知