若图中出现负权的边,dijstra就会失效,此时可以使用该算法(优化的该算法在后面讲到)
算法过程:
- 数组Dis[i]记录源点到i的最短距离,s为源点,初始化Dis[i]=无穷大,Dis[s]=0
- 设n为顶点数,循环n-1次,对于每一条边e(u,v),如果Dis[u]+e(u,v) < Dis[v],则Dis[v]=Dis[u]+e(u,v)。
- 为检测图中是否有负环:对于每一条边e(u,v),如果存在Dis[u]+e(u,v)< Dis[v](松弛过的所有边还可以再松弛),则图中存在负环路,不存在单源最短路径。
代码如下(有向图):
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int num=1000;
struct Edge
{
int u,v;//边的起点和终点
int weight;//该边的权重
};
Edge edge[num];
int Dis[num];
int n;
int m;
void init()
{
//结点个数,边的个数,源点
int s;
cin>>n>>m>>s;
for(int i=1; i<=n; i++)
{
Dis[i]=INF;
}
Dis[s]=0;
for(int i=1; i<=m; i++)
{
cin>>edge[i].u>>edge[i].v>>edge[i].weight;
if(edge[i].u==s)
Dis[edge[i].v]=edge[i].weight;
}
}
bool Bellman_Ford()
{
for(int i=1; i<n; i++)
{
for(int j=1; j<=m; j++)
{
if(Dis[edge[j].v]>Dis[edge[j].u]+edge[j].weight)
{
Dis[edge[j].v]=Dis[edge[j].u]+edge[j].weight;
}
}
}
bool flag=true;
for(int j=1; j<=m; j++)
{
if(Dis[edge[j].v]>Dis[edge[j].u]+edge[j].weight)
{
flag=false;
break;
}
}
if(flag==false)
{
return false;
}
return true;
}
int main()
{
init();
if(Bellman_Ford())
{
for(int i=1;i<=n;i++)
{
cout<<Dis[i]<<" ";
}
cout<<endl;
}
else
{
cout<<"存在负环"<<endl;
}
}
为什么要循环n-1次:
最短路径一定是一个简单路径,不可能包含回路。如果包含回路,且权值均为正,那么一定可以去掉回路得到更短的路径。如果权值有负值,那么本身就无解。
图中有n个点,且不包含回路,那么最短路径最多有n-1条边。所以最多松弛n-1次