zoukankan      html  css  js  c++  java
  • 次短路 第K短路

    次短路

    次短路的概念其实很简单,我们都知道可以使用Dijkstra、SPFA等求一个点到图中其他点的最短路,而次短路就是除了最短路第二短的路

    我们的思路还是使用Dijkstra+堆优化,最短路的基础上,次短路可以由次短路+边更新,也可以由最短路+边 跟新,这里注意一点,因为次短路更新时也会对其它次短路产生影响,所以更新次短路时也需要入队我们先尝试更新最短路,成功的话就把原来的最短路给次短路,不成功的话就单独尝试更新次短路

    例题

    https://www.luogu.com.cn/problem/P2865

    贝茜把家搬到了一个小农场,但她常常回到FJ的农场去拜访她的朋友。贝茜很喜欢路边的风景,不想那么快地结束她的旅途,于是她每次回农场,都会选择第二短的路径,而不象我们所习惯的那样,选择最短路。 贝茜所在的乡村有R(1<=R<=100,000)条双向道路,每条路都联结了所有的N(1<=N<=5000)个农场中的某两个。贝茜居住在农场1,她的朋友们居住在农场N(即贝茜每次旅行的目的地)。 贝茜选择的第二短的路径中,可以包含任何一条在最短路中出现的道路,并且,一条路可以重复走多次。当然咯,第二短路的长度必须严格大于最短路(可能有多条)的长度,但它的长度必须不大于所有除最短路外的路径的长度。

     分析

    在Dijkstra的基础上,我们将dis一维数组改为dis【maxn】【2】二维数组,dis【maxn】【0】表示最短路,dis【maxn】【1】表示次短路,同时将vis访问标记数组也修改为二维

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int n, r,s=1;
    #define maxn 5001
    #define maxm 100001
    #define inf 0x3f3f3f3f
    struct edge
    {
        int to;
        int dis;
        int next;
    }e [maxm*2]; //注意这里!!本题的路径是双向的,所以它的邻接表的大小要在maxm的基础上乘2,不然会RE
    struct node
    {
        int dis;
        int pos;
        int kind;
        bool operator <(const node &x)const
        {
            return dis > x.dis;
        }
    };
    int head[maxn], dis[maxn][2], vis[maxn][2],cnt=0;
    priority_queue<node>q;
    void addedge(int u,int v, int w)
    {
        cnt++;
        e[cnt].to = v;
        e[cnt].dis = w;
        e[cnt].next = head[u];
        head[u] = cnt;
    }
    void Dijkstra()
    {
        memset(vis, 0, sizeof(vis));
        memset(dis, inf, sizeof(dis));
        dis[s][0] = 0;//dis的初始化
        q.push({dis[s][0],s,0});
        while (!q.empty())
        {
            int x = q.top().pos; int kind = q.top().kind; q.pop();
            if (vis[x][kind])continue;
            vis[x][kind] = 1;
            for (int i = head[x]; i; i = e[i].next)
            {
                int y = e[i].to;
                if (dis[y][0] > dis[x][kind] + e[i].dis)//dis[x][kind] + e[i].dis是我们试探的x节点是否能缩短起点到y节点的距离,如果dis[y][0] > dis[x][kind] + e[i].dis表示能将距离缩短
                {
                    dis[y][1] = dis[y][0];//将此时的最短路更新为次短路
                    q.push({ dis[y][1] ,y,1});//入队
                    dis[y][0] = dis[x][kind] + e[i].dis;//将更短的更新为最短路
                    q.push({ dis[y][0] ,y,0 });//入队
                }
                else if (dis[y][1] > dis[x][kind] + e[i].dis)//如果只是比次短路短,那么只更新次短路
                {
                    dis[y][1] = dis[x][kind] + e[i].dis;
                    q.push({ dis[y][1] ,y,1 });
                }
            }
        
        }
    
    }
    
    int main()
    {
        scanf("%d%d", &n, &r);
        int a, b, c;
        for (int i = 0; i < r; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            addedge(a, b, c);//两个:双向路
            addedge(b, a, c);
        }
        Dijkstra();
        printf("%d", dis[n][1]);
    
    }

    第K短路

    同理,第K短路的意思就是输出第K短路的长度

    算法——A*

    A* 算法(这里的* 英文就读作star),是一种启发式搜索的方法,它离我们并不遥远,常用的BFS就是A*算法的一种特例。

    为启发式算法中很重要的一种,被广泛应用在最优路径求解和一些策略设计的问题中。而A*算法最为核心的部分,就在于它的一个估值函数的设计上:
    f(n)=g(n)+h(n)
    其中f(n)是每个可能试探点的估值,它有两部分组成:一部分为g(n),它表示从起始搜索点到当前点的代价(通常用某结点在搜索树中的深度来表示)。另一部分,即h(n),它表示启发式搜索中最为重要的一部分,即当前结点到目标结点的估值,h(n)设计的好坏,直接影响着具有此种启发式函数的启发式算法的是否能称为A*算法。
    在使用A*算法进行解题的时候,我们将起始点到图中的某一点的距离作为g函数,将该某一点到终点的距离作为h函数

    g函数的计算:我们使用简单的BFS进行正向的计算

    h函数的计算:我们使用Dijkstra算法进行反向图的距离计算:这里如果是单向图,我们就要建立反向图,如果是双向图的话就不用了,直接在原图求到终点的距离就行。

    上面例题的A*代码

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<string>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define maxn 5001                    
    #define maxm 100001
    struct edge
    {
        int to;
        int dis;
        int next;
    }e[maxm*2],e2[maxm*2];//注意如果是双向路的话要*2!!!!
    struct node//A*算法中使用
    {
        int dis;//正向图起始点到该点的距离
        int pos;//下标
        int h;//反向图中该点到重点的e距离
        bool operator<(const node& x)const
        {
            if (dis + h == x.dis + x.h)return dis>x.dis;
            else return dis + h > x.dis + x.h;
        }
    };
    struct node2//Dijkstra中使用
    {
        int dis;
        int pos;
        bool operator<(const node2& x)const
        {
            return dis > x.dis;
        }
    };
    int head[maxn],head2[maxn], cnt = 0,cnt2=0, vis[maxn],dis[maxn];
    int n, m, t, s,k;
    void addedge(int u,int v,int w)//正向图
    {
        cnt++;
        e[cnt].to = v;
        e[cnt].dis = w;
        e[cnt].next = head[u];
        head[u] = cnt;
    }
    void addedge2(int u, int v, int w)//反向图
    {
        cnt2++;
        e2[cnt2].to = v;
        e2[cnt2].dis = w;
        e2[cnt2].next = head2[u];
        head2[u] = cnt2;
    }
    void Dijkstra()
    {
        memset(dis, inf, sizeof(dis));
        dis[t] = 0;
        priority_queue<node2>q;
        q.push({0, t});
        while (!q.empty())
        {
            int x = q.top().pos; q.pop();
            if (vis[x])continue;
            vis[x] = 1;
            for (int i = head2[x]; i; i = e2[i].next)
            {
                int y = e2[i].to;
                if (dis[y] > dis[x] + e2[i].dis)
                {
                    dis[y] = dis[x] + e2[i].dis;
                    q.push({dis[y],y});
                }
            }
        }
    }
    
    int astar(int s, int t, int k)//S起点 t终点 k 第k短
    {
        if (dis[s] ==inf)return -1;
        if (s == t)k++;//这里kjwyi的原因是如果起点与终点相同的话,意思就是出发最后返回到原点,我们要避免距离为0的情况
        priority_queue<node>q;
        q.push({0,s,dis[s]});
        int amount = 0;//出队的次数(队列已经进行了大小的排序,第k次出队就是第k短的路)
        while (!q.empty())
        {
            int x = q.top().pos; int len = q.top().dis; q.pop();
            if (x == t)amount++;//最后x为终点表示已经导论路径终点,此条路径长度已经计算完毕结束
            if (amount == k)return len;
            for (int i = head[x]; i; i = e[i].next)
            {
                int y = e[i].to;
                q.push({len+e[i].dis,y,dis[y]});//一个简单的BFS
            }
        }
        return -1;
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            addedge(a, b, c);//双向图
            addedge(b,a, c);
            addedge2(b, a, c);
            addedge2(a,b, c);
        }
        //scanf("%d%d%d", &s, &t, &k);
        s = 1; t = n; k = 2;
        Dijkstra();
        printf("%d
    ", astar(s, t, k));
    }
    
    
    //5 5
    //1 2 5
    //2 3 5
    //3 4 4
    //4 5 6
    //1 5 21
    
    //21

    例题2

    http://poj.org/problem?id=2449

     代码

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<string>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define maxn 1001
    #define maxm 100001
    struct edge
    {
        int to;
        int dis;
        int next;
    }e[maxm],e2[maxm];
    struct node
    {
        int dis;
        int pos;
        int h;
        bool operator<(const node& x)const
        {
            if (dis + h == x.dis + x.h)return dis>x.dis;
            else return dis + h > x.dis + x.h;
        }
    };
    struct node2
    {
        int dis;
        int pos;
        bool operator<(const node2& x)const
        {
            return dis > x.dis;
        }
    };
    int head[maxn],head2[maxn], cnt = 0,cnt2=0, vis[maxn],dis[maxn];
    int n, m, t, s,k;
    void addedge(int u,int v,int w)
    {
        cnt++;
        e[cnt].to = v;
        e[cnt].dis = w;
        e[cnt].next = head[u];
        head[u] = cnt;
    }
    void addedge2(int u, int v, int w)
    {
        cnt2++;
        e2[cnt2].to = v;
        e2[cnt2].dis = w;
        e2[cnt2].next = head2[u];
        head2[u] = cnt2;
    }
    void Dijkstra()
    {
        memset(dis, inf, sizeof(dis));
        dis[t] = 0;
        priority_queue<node2>q;
        q.push({0, t});
        while (!q.empty())
        {
            int x = q.top().pos; q.pop();
            if (vis[x])continue;
            vis[x] = 1;
            for (int i = head2[x]; i; i = e2[i].next)
            {
                int y = e2[i].to;
                if (dis[y] > dis[x] + e2[i].dis)
                {
                    dis[y] = dis[x] + e2[i].dis;
                    q.push({dis[y],y});
                }
            }
        }
    }
    
    int astar(int s, int t, int k)
    {
        if (dis[s] ==inf)return -1;
        if (s == t)k++;
        priority_queue<node>q;
        q.push({0,s,dis[s]});
        int amount = 0;
        while (!q.empty())
        {
            int x = q.top().pos; int len = q.top().dis; q.pop();
            if (x == t)amount++;
            if (amount == k)return len;
            for (int i = head[x]; i; i = e[i].next)
            {
                int y = e[i].to;
                q.push({len+e[i].dis,y,dis[y]});
            }
        }
        return -1;
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            addedge(a, b, c);
            addedge2(b, a, c);
        }
        scanf("%d%d%d", &s, &t, &k);
        Dijkstra();
        printf("%d
    ", astar(s, t, k));
    }

    参考:https://blog.csdn.net/xiaonanxinyi/article/details/97961914?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

    https://blog.csdn.net/Z_Mendez/article/details/47057461?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

  • 相关阅读:
    Codeforces Gym 100571A A. Cursed Query 离线
    codeforces Gym 100500 J. Bye Bye Russia
    codeforces Gym 100500H H. ICPC Quest 水题
    codeforces Gym 100500H A. Potion of Immortality 简单DP
    Codeforces Gym 100500F Problem F. Door Lock 二分
    codeforces Gym 100500C D.Hall of Fame 排序
    spring data jpa 创建方法名进行简单查询
    Spring集成JPA提示Not an managed type
    hibernate配置文件中的catalog属性
    SonarLint插件的安装与使用
  • 原文地址:https://www.cnblogs.com/Jason66661010/p/13140312.html
Copyright © 2011-2022 走看看