1. 程式人生 > 實用技巧 >模板-Bellman-Ford&SPFA

模板-Bellman-Ford&SPFA

Bellman-Ford演算法

求最短路的

這個演算法基於一個叫做“鬆弛”的操作

鬆弛會試圖往最短路中加邊來縮短最短路

對於這樣一個圖

1到3的最短路顯然是1→2→3而不是1→3繞遠路就是最短的捷徑

我們所進行的鬆弛操作就是這樣的

鬆弛時列舉每一條邊,並判斷先走最短路到達這條邊的u點,再經過這條邊到達v點,能否使到v點的最短路縮小

若可以,就將到v點的最短路更新

顯然,只用一次鬆弛不能夠得到完整的最短路,所以要進行多次鬆弛

經過一大堆亂七八糟的玄學證明,我們知道只需要進行n-1次鬆弛即可

所以,當n=1時,我們只要進行0次鬆弛即可

n=2時,1次即可

n=3,3次即可

n=2147483647,2147483646即可

不行這個數太大了

實際上我們發現,n-1是最大次數

實際上有效鬆弛有可能沒有這麼多

所以我們判斷如果這次鬆弛沒有讓最短路發生變化,就直接結束演算法

對於負權環,由於正常情況下最多進行n-1次有效鬆弛,如果在n-1次全部完成後還可以鬆弛,就可以說明這裡面有負權環了

程式碼:

#include <bits/stdc++.h>
using namespace std;
int n,m;
struct Edge
{
    int u,v,w,nxt;
}e[10];
int h[10],cnt;
int dis[10];
int bak[10];
void add(int u,int v,int w)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=h[u];
    h[u]=cnt;
}
int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
    }
    for(int i=1;i<=n;i++)
        dis[i]=0x3f;
    dis[1]=0;
    for(int k=1;k<=n-1;k++)
    {
        for(int i=1;i<=n;i++)
            bak[i]=dis[i];
        for(int i=1;i<=m;i++)
            if(dis[e[i].v]>dis[e[i].u]+e[i].w)
                dis[e[i].v]=dis[e[i].u]+e[i].w;
        int check=0;
        for(int i=1;i<=n;i++)
            if(bak[i]!=dis[i])
            {
                check=1;
                break;
            }
        if(!check)
            break;
    }
    int flag=0;
    for(int i=1;i<=m;i++)
        if(dis[e[i].v]>dis[e[i].u]+e[i].w)
            flag=1;
    if(flag==1)
        cout << "無最短路" << endl;
    else
    {
        for(int i=1;i<=n;i++)
            cout << dis[i] << ' ';
    }
    return 0;
}

但是我們回頭看看這個程式碼

鬆弛時列舉每一條邊

每一條

根據我的經驗,我懷疑這裡面有可能有許多沒用的鬆弛

實際上我們可以通過一大堆亂七八糟的玄學證明,鬆弛只有可能令一部分點的最短路縮小

這一部分點就是鬆弛時所加入的那條邊的v點所連著的點

於是我們就接觸到了另一個演算法,另一個在Bellman-Ford演算法基礎上的最短路演算法——

Shortest Path Faster Algorithm

它利用佇列,每次取出隊首,列舉隊首所有的邊進行鬆弛

並將那些鬆弛成功的邊的v點入隊

以達到避免無效鬆弛的效果

程式碼:

#include <bits/stdc++.h>
using namespace std;
int n,m;
struct Edge
{
    int u,v,w,nxt=-1;
}e[10];
int h[10],cnt;
int dis[10];
int bak[10];
void add(int u,int v,int w)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=h[u];
    h[u]=cnt;
}
queue<int> q;
int bk[10];
int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
    }
    for(int i=1;i<=n;i++)
        dis[i]=999999;
    dis[1]=0;
    for(int i=1;i<=n;i++)
        bk[i]=0;
    q.push(1);
    bk[1]=1;
    while(!q.empty())
    {
        int k=h[q.front()];
        while(k!=-1)
        {
            if(dis[e[k].v]>dis[e[k].u]+e[k].w)
            {
                dis[e[k].v]=dis[e[k].u]+e[k].w;
                if(bk[e[k].v]==0)
                {
                    q.push(e[k].v);
                    bk[e[k].v]=1;
                }
            }
            k=e[k].nxt;
        }
        bk[q.front()]=0;
        q.pop();
    }
    for(int i=1;i<=n;i++)
        cout << dis[i] << ' ';
    return 0;
}