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 即结点数乘以边数,时间复杂度较大。