bellman-ford算法是用於解決單源最短路徑的算法,與Dijkstra不同的是,它可解決存在負權邊的的情況。同時,它也可以檢測是否存在存在負權值環。
其基本思路如下:
建立一個距離數組dis[],將源點設爲0,其餘點的距離初始化爲無窮大。
將下列操作循環最多n-1次:
對於每條邊,start->end,
if(dis[end] > dis[start] + weight)
dis[end] = dis[start] + weight;
(該操作稱爲一次"鬆弛"操作)
循環n-1次或沒有任何一條邊能夠經過鬆弛操作可以縮短距離。
接下來就是bellman-ford算法最精彩的地方。我們再循環一次,對每條邊再進行一次鬆弛操作,如果還能縮短距離,說明該圖中存在負權環,也就不存在最短路徑。
對其理解:
由於距離數組一開始除了源點外,距離都設置爲無窮大,所以第一次循環時,必然是鬆弛與起點相連的邊。
其基本內涵相當於從起點開始,不斷向外擴展。
如果起點到某點i存在一條最短路徑的話,就相當於從源點開始,每次都找到從起點到i點最短路徑上的一條邊,類似與dfs的思想。
由於最短路徑最多有n-1條邊,所以最多循環n-1次。同理,如果循環了n-1次後,仍能縮短距離,說明圖中存在負權環。
以下是有向圖的代碼,無向圖可將邊數乘2當做有向圖做。
注意,如果無向圖中存在一條無向的負邊(源點可達),說明是不存在最短路徑的。
代碼:
#include <iostream>
#define INF 999999999
using namespace std;
class Edge
{
public:
int s,e;
int weight;
};
int main()
{
int n;
int N,M,W;
int k;
cin>>n;
cin>>N>>M>>W;
int *dis = new int [N+1]; //距離數組
Edge *edge = new Edge [2*M+N]; //M條無向邊,W條有向表
bool flag = true;
while(n--)
{
for(int i=1;i<=N;i++)
dis[i] = INF;
dis[1] = 0; //初始化距離數組
flag = true;
k=0;
for(int i=0;i<M;i++)//將無向邊轉爲兩條有向邊
{
int s,e,weight;
cin>>s>>e>>weight;
edge[k].s = s,edge[k].e = e,edge[k++].weight=weight; //儲存兩條邊
edge[k].s = e,edge[k].e = s,edge[k++].weight=weight;
}
for(int i=0;i<W;i++)//儲存有向邊
{
int s,e,weight;
cin>>s>>e>>weight;
edge[k].s = s,edge[k].e = e,edge[k++].weight = -weight;
}
for(int i=0;i<N-1;i++)
{
flag = true;
for(int j=0;j<2*M+W;j++)
{
if(dis[edge[j].e] > dis[edge[j].s] + edge[j].weight)
{
dis[edge[j].e] = dis[edge[j].s] + edge[j].weight;
flag = false;
}
}
if(flag)
break;
}
flag = false;
for(int j=0;j<2*M+W;j++)
{
if(dis[edge[j].e] > dis[edge[j].s] + edge[j].weight)
{
dis[edge[j].e] = dis[edge[j].s] + edge[j].weight;
flag = true;
break;
}
}
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
可以看出 時間複雜度爲V*E 即結點數乘以邊數,時間複雜度較大。