1. 程式人生 > >第k短路和次短路

第k短路和次短路

一.求第k短路

1.什麼是第k短路?第一短路就是最短路,以此類推。求某點s到某點e的第k短路就是k短路問題

2.思路:

(1)我們知道在BFS中,第一次到達終點就是到終點的最短路,那麼第k次到達終點,當然就是到終點的第k短路了。但是如果直接BFS搜尋下去,時間複雜度會非常高,因此我們需要剪枝,怎麼剪枝呢?

(2)我們每次只需要取出每次到達終點最有希望的路徑,就避開了一些沒有意義的到其他點的路徑。因此我們需要一個啟發函式。令f = x + h(其中x為到當前點的實際距離,h為從當前點到達終點的估測最短距離),則f就估測為從起點到終點的路徑長度,我們每次只要有目的有方向的前進到達終點k次即為k短路

(3)那麼怎麼求這個h呢?h其實為每個點到達終點的最短路,但是我們只學過某個點到其他點的最短路怎麼辦?當然是把終點當作起點跑最短路啊(哇笨蛋) , 但是這裡有一個問題:我們需要在跑終點最短路時使用反向邊,跑BFS時使用正向邊(有向圖),為什麼呢:

(起點1,終點2)

我們如果跑終點最短路使用正向邊,2是到不了1的,所以在跑BFS時,從1到2的估測函式是不存在的,但是事實是存在的。所以我們這裡應該使用反向建邊。而跑BFS時當然是使用正向邊。

也就是說,終點反向建邊能到達的點,正向邊時才是能過來的點



3.求解步驟

(1)Dijkstra求終點到其他點的最短路

(2)正向邊跑起點的BFS(以A*啟發函式:f = x + h為排序,取出點)

4.程式碼:(poj2499)板子:

#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;
}

二.求次短路

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 ]時,更新次短路

3.程式碼:

#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(d,edge[i].to));
             }
             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;
}