• 次短路 第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

  • 相关阅读:
    list转map,set,使用stream进行转化
    使用thymeleaf模板实现博客评论的异步刷新
    python 字典元组集合字符串
    python列表解析补充:
    python列表解析
    springboot框架中集成thymeleaf引擎,使用form表单提交数据,debug结果后台获取不到数据
    配置github——每次提交后使contributions有记录(有小绿格子)
    github浏览器无法访问,并且idea无法push项目
    互联网协议入门
    http协议入门---转载
  • 原文地址:https://www.cnblogs.com/Jason66661010/p/13140312.html
Copyright © 2020-2023  润新知