【图论】 第K短路和次短路

目录

一. 第K短路

1. 思路分析

2. 求解步骤

3. 实现代码

二. 次短路

1. 求解步骤

2. 实现代码


一. 第K短路

第K短路问题是最短路问题的特殊情况,其中第一短路就是最短路,并以此类推;求某源点s到某终点e的第K短路就是K短路问题。

1. 思路分析

        我们知道在BFS中,第一次搜索到达终点的路径就是到终点的最短路,那么第k次到达终点的路径当然就是到终点的第k短路了。但是如果直接按照BFS搜索下去,那么时间复杂度会非常高,因此我们需要对这个思路进行剪枝,具体怎么剪枝呢?

        参照启发式搜索的思想,我们每次只需要取出每次到达终点最有希望的路径,这样就避开了一些没有意义的到其他点的路径。因此我们需要一个启发函数,令 f = x + h(其中x为到当前点的实际距离,h为从当前点到达终点的估测最短距离),则f就估测为从起点到终点的路径长度,我们每次只要有目的有方向的前进到达终点k次即为k短路。

        那么怎么求这个h呢?h其实为每个点到达终点的最短路,但是我们只学过某个点到其他点的最短路怎么办?当然是可以把终点当作起点来跑最短路!但是这里有一个问题:我们需要在跑终点最短路时使用反向边,跑BFS时使用正向边(有向图),具体原因可以参考如下(起点1,终点2):

        我们如果跑终点最短路使用正向边,2是到不了1的;所以在跑BFS时,从1到2的估测函数是不存在的,但是事实是存在的。所以我们这里应该使用反向建边,而跑BFS时当然是使用正向边。也就是说,终点反向建边能到达的点,正向边时才是能过来的点。

2. 求解步骤

(1)通过Dijkstra最短路算法求终点到其他点的最短路;

(2)正向边跑从起点出发的BFS算法(以A*启发函数:f = x + h为排序,取出点);

3. 实现代码

//poj2449
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
typedef pair<LL,int> P;
const int maxn = 1000 + 7;
struct Edge{//正向边
    int to,next;
    LL val;
}edge[maxn*100];
struct Line{//反向边
    int to,next;
    LL val;
}line[maxn*100];
int n,m,s,e,tot,k,head[maxn],revhead[maxn];
int tot2;
bool vis[maxn];
LL dist[maxn];//保存终点到其他点的最短路
inline void addEdge(int a,int b,LL c){//正向建边
    edge[tot].to = b;edge[tot].next =  head[a];edge[tot].val = c;head[a] = tot++;
}
inline void AddEdge(int a,int b,LL c){//反向建边
   line[tot2].to = b;line[tot2].next = revhead[a];line[tot2].val = c;revhead[a] = tot2++;
}
struct Node{//BFS保存状态
   int to;
   LL cost;
   bool operator <(const Node&another)const{//排序规则按照估价函数大小由小到大
        return cost + dist[to] > another.cost + dist[another.to];//估价= 当前 + 到终点最短
   }
   Node(int a,LL c):to(a),cost(c) {}
};
inline void Dijkstra(int a){//最短路
   dist[a] = 0;
   priority_queue<P,vector<P>,greater<P> >que;
   que.push(P(0,a));
   while(!que.empty()){
       P p = que.top();
       que.pop();
       if(vis[p.second])continue;
       vis[p.second] = 1;
       LL num = p.first;
       for(int i = revhead[p.second];~i;i = line[i].next){//跑反向边
           if(!vis[line[i].to]&&dist[line[i].to] > num + line[i].val){
               dist[line[i].to] = num + line[i].val;
               que.push(P(dist[line[i].to],line[i].to));
           }
       }
   }
}
inline LL BFS(int a){//BFS
   priority_queue<Node> que;
   que.push(Node(a,0));
   while(!que.empty()){
      Node node = que.top();
      que.pop();
      if(node.to==e){//到达终点次数
         k--;
         if(k==0){
            return node.cost;
         }
      }
      for(int i = head[node.to];~i;i = edge[i].next){//扩散(跑反向边)
           que.push(Node(edge[i].to,node.cost + edge[i].val));
      }
   }
   return -1;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        tot = tot2 = 0;
        memset(dist,INF,sizeof(dist));
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        memset(revhead,-1,sizeof(revhead));
        for(int i = 0;i<m;i++){
            int a,b;
            LL v;
            scanf("%d%d%lld",&a,&b,&v);
            addEdge(a,b,v);
            AddEdge(b,a,v);
        }
        scanf("%d%d%d",&s,&e,&k);//起点 + 终点 + k短路
        Dijkstra(e);
        if(dist[s]==INF){
            printf("-1\n");
            continue;
        }
        if(s==e)k++;//起点终点重合,排除0距离
        LL ans = BFS(s);
        printf("%lld\n",ans);
    }
    return 0;
}

二. 次短路

次短路即第2短路,相比第K短路问题更加特殊,当然也可以直接通过第K短路求解方法来解决;但除此之外,次短路问题还有更加灵活的解决方法。

1. 求解步骤

(1)dist[i][0] 表示到点 i 的最短路,dist[i][1]表示到点 i 的次短路。

(2)路径更新条件:

  • 最短路更新:当dist[i][0] > len(j) + val(j,i)时,直接更新最短路;
  • 次短路更新:当dist[i][0] < len(j) + val(j,i) < dist[i][1]时,则更新次短路;

2. 实现代码

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int,int>P;
const int maxn = 100000 + 7;
struct Edge{
   int to,next,val;
}edge[maxn];
int n,m,head[maxn],dist[maxn][2],tot;
void addEdge(int a,int b,int c){
   edge[tot].to = b;edge[tot].val = c;edge[tot].next = head[a];head[a] = tot++;
}
void Dijkstra(int s){
    for(int i = 0;i<=n;i++)dist[i][0] = dist[i][1] = INF;
    dist[s][0] = 0;
    priority_queue<P,vector<P>, greater<P> >que;
    que.push(P(0,s));
    while(!que.empty()){
        P p = que.top();
        que.pop();
        if(p.first > dist[p.second][1])continue;
        for(int i = head[p.second];~i;i = edge[i].next){
            int d = p.first + edge[i].val;
             if(dist[edge[i].to][0] > d){//更新最短路
                swap(dist[edge[i].to][0] , d);//交换!!!
                que.push(P(dist[edge[i].to][0],edge[i].to));//注意d值已经被交换了
             }
             if(dist[edge[i].to][1] > d&&dist[edge[i].to][0] < d){//更新次短路
                dist[edge[i].to][1] = d;
                que.push(P(d,edge[i].to));
             }
        }
    }
}
int main()
{
    tot = 0;
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i = 0;i<m;i++){
        int a,b,v;
        scanf("%d%d%d",&a,&b,&v);
        addEdge(a,b,v);
        addEdge(b,a,v);
    }
    int s,t;
    scanf("%d%d",&s,&t);
    Dijkstra(s);
    printf("%d %d\n",dist[t][0],dist[t][1]);
    return 0;
}
  • 13
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
引用\[1\]提供了使用Python的networkx库绘制网络图和计算最短加权路径的示例代码。该代码使用了一个包含顶点和边的列表,并使用add_nodes_from和add_weighted_edges_from方法将它们添加到图中。然后,使用nx.shortest_path_length方法计算了从顶点v1到顶点v11的最短加权路径长度为13。\[1\] 引用\[2\]提供了一个计算最短路径的Python程序示例。该程序使用了numpy和networkx库。首先,定义了一个包含顶点和边的列表,并使用add_nodes_from和add_weighted_edges_from方法将它们添加到图中。然后,使用nx.shortest_path_length方法计算了最短路径长度,并将结果存储在一个字典中。接下来,使用numpy创建了一个6x6的零矩阵,并使用两个嵌套的for循环将最短路径长度填充到矩阵中。最后,使用矩阵乘法计算了运力,并找到了最小运力和对应的位置。\[2\] 引用\[3\]提供了关于Dijkstra算法的一些背景信息。Dijkstra算法是一种寻找最短路径的算法,适用于所有权重大于等于0的情况。它可以用于解决从一个起始点到任意一个点的最短路径问题。\[3\] 综上所述,如果你想在Python中计算图论中的最短路径,可以使用networkx库和Dijkstra算法。你可以根据引用\[1\]和引用\[2\]中的示例代码进行操作。 #### 引用[.reference_title] - *1* *3* [运筹学——图论与最短距离(Python实现)](https://blog.csdn.net/weixin_46039719/article/details/122521276)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [数学建模:图论模型 — 最短路模型示例 (Python 求解)](https://blog.csdn.net/qq_55851911/article/details/124776487)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿阿阿安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值