1. 程式人生 > >關於 Bellman-Ford 與 Floyd 演算法的一點感想

關於 Bellman-Ford 與 Floyd 演算法的一點感想

在四種常用的最短路演算法 Dijkstra, SPFA, floyd, Bellman-Ford 中, Dijks 和 SPFA 的使用較為普遍, 對大多數人來說, 也較為熟悉. 然而, floyd 與 BF 演算法在一些特定的情況下也是非常管用的, 因此有必要在這裡作出一點總結.
Floyd的基本思路就是列舉任意兩個點i, j, 再列舉任意的第三個點k, 用d[i][k] + d[j][k] 來鬆弛d[i][j]的值. 時間複雜度為O(n ^ 3), 優點在於可以求出任意兩點之間的距離, 在稠密圖中也非常管用.

#include<iostream>
#include<vector>
using namespace std; const int &INF=100000000; void floyd(vector<vector<int> > &distmap,//可被更新的鄰接矩陣,更新後不能確定原有邊 vector<vector<int> > &path)//路徑上到達該點的中轉點 //福利:這個函式沒有用除INF外的任何全域性量,可以直接複製! { const int &NODE=distmap.size();//用鄰接矩陣的大小傳遞頂點個數,減少引數傳遞 path.assign(NODE,vector
<int>
(NODE,-1));//初始化路徑陣列 for(int k=1; k!=NODE; ++k)//對於每一箇中轉點 for(int i=0; i!=NODE; ++i)//列舉源點 for(int j=0; j!=NODE; ++j)//列舉終點 if(distmap[i][j]>distmap[i][k]+distmap[k][j])//不滿足三角不等式 { distmap[i][j]=distmap[i][k]+distmap[k][j];//更新
path[i][j]=k;//記錄路徑 } } void print(const int &beg,const int &end, const vector<vector<int> > &path)//傳引用,避免拷貝,不佔用記憶體空間 //也可以用棧結構先進後出的特性來代替函式遞迴 { if(path[beg][end]>=0) { print(beg,path[beg][end],path); print(path[beg][end],end,path); } else cout<<"->"<<end; } int main() { int n_num,e_num,beg,end;//含義見下 cout<<"(不處理負權迴路)輸入點數、邊數:"; cin>>n_num>>e_num; vector<vector<int> > path, distmap(n_num,vector<int>(n_num,INF));//預設初始化鄰接矩陣 for(int i=0,p,q; i!=e_num; ++i) { cout<<"輸入第"<<i+1<<"條邊的起點、終點、長度(100000000代表無窮大,不聯通):"; cin>>p>>q; cin>>distmap[p][q]; } floyd(distmap,path); cout<<"計算完畢,可以開始查詢,請輸入出發點和終點:"; cin>>beg>>end; cout<<"最短距離為"<<distmap[beg][end]<<",列印路徑:"<<beg; print(beg,end,path); }

Bellman-Ford是SPFA演算法的前身, 時間複雜度為O(VE)…非常慢, 但是可以用於判斷負權迴路. 常用於差分約束系統中. Bellman-Ford這種寫法相當暴力, 直接迴圈nodeNum次, 每次列舉每一條邊, 假如這條邊可以用於鬆弛源點到端點的距離, 則進行鬆弛. 至於判斷負環, 再列舉一遍所有邊, 假如存在邊仍能用於鬆弛, 則說明存在負權迴路.

#include<iostream>  
#include<cstdio>  
using namespace std;  

#define MAX 0x3f3f3f3f  
#define N 1010  
int nodenum, edgenum, original; //點,邊,起點  

typedef struct Edge //邊  
{  
    int u, v;  
    int cost;  
}Edge;  

Edge edge[N];  
int dis[N], pre[N];  

bool Bellman_Ford()  
{  
    for(int i = 1; i <= nodenum; ++i) //初始化  
        dis[i] = (i == original ? 0 : MAX);  
    for(int i = 1; i <= nodenum - 1; ++i)  
        for(int j = 1; j <= edgenum; ++j)  
            if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //鬆弛(順序一定不能反~)  
            {  
                dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;  
                pre[edge[j].v] = edge[j].u;  
            }  
            bool flag = 1; //判斷是否含有負權迴路  
            for(int i = 1; i <= edgenum; ++i)  
                if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)  
                {  
                    flag = 0;  
                    break;  
                }  
                return flag;  
}  

void print_path(int root) //列印最短路的路徑(反向)  
{  
    while(root != pre[root]) //前驅  
    {  
        printf("%d-->", root);  
        root = pre[root];  
    }  
    if(root == pre[root])  
        printf("%d\n", root);  
}  

int main()  
{  
    scanf("%d%d%d", &nodenum, &edgenum, &original);  
    pre[original] = original;  
    for(int i = 1; i <= edgenum; ++i)  
    {  
        scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);  
    }  
    if(Bellman_Ford())  
        for(int i = 1; i <= nodenum; ++i) //每個點最短路  
        {  
            printf("%d\n", dis[i]);  
            printf("Path:");  
            print_path(i);  
        }  
    else  
        printf("have negative circle\n");  
    return 0;  
}