Bellman-Ford算法
这个算法解决的是一般情况下的单源最短路径问题,即可带负权值,但是缺点是效率低。Bellman-Ford算法返回一个布尔值,以表明是否存在一个从源结点可以到达的权重为负值的环路。
它通过对边进行松弛操作来渐进地降低从源结点s到每个结点v的最短路径的估计值v.d,直到该估计值与实际的最短路径权重δ(s,v)相同时为止。
伪代码如下:
BELLMAN-FORD(G,W,s)
INITALIZE-SINGLE-SOURCE(G,s)
for i=1 to |G.v|-1
for each edge(u,v)∈G.E
RELAX(u,v,w)
for each edge(u,v)∈G.E
if v.d>u.d+w(u.v)
return FALSE
return TRUE
但是我习惯用《挑战程序设计》中的Bellman-Ford的代码。判断是否存在负圈可另外写一函数。
代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1000;
const int INF = 10000000;
int n,e;
int d[maxn],pre[maxn];
struct edge{
int from,to,cost;
};
edge es[maxn];
void init(int s)
{
for(int i=0; i<n; i++)
{
d[i] = INF;
pre[i] = NULL;
}
d[s] = 0;
return;
}
void Bellman_Ford(int s)
{
init(s);
while(true)
{
bool updata = false;
for(int i=0; i<e; i++)
{
edge e = es[i];
if(d[e.from]!=INF && d[e.to] > d[e.from] +e.cost)
{
d[e.to] = d[e.from] + e.cost;//更新最短路径
pre[e.to] = e.from; //更新父结点
updata = true;
}
}
if(!updata) break;
}
}
int main()
{
scanf("%d%d",&n,&e);
int s,t,c;
for(int i=0; i<e; i++)
{
scanf("%d%d%d",&s,&t,&c);
es[i].from = s;
es[i].to = t;
es[i].cost = c;
}
Bellman_Ford(0);
for(int i=0; i<n; i++)
{
printf("%d ",d[i]);
}
printf("\n");
for(int i=0; i<n; i++)
{
printf("%d ",pre[i]);
}
return 0;
}
如果在图中不存在从s可达的负圈,那么最短路不会经过同一个顶点两次
判断是否有负圈
/*判断是否存在负圈*/
bool find_negative_loop()
{
memset(d,0,sizeof(d));
for(int i=0; i<n; i++)
{
for(int j=0; j<e; j++)
{
egde e = es[j];
if(d[e.to] > d[e.from] + e.cost)
{
d[e.to] = d[e.from] + e.cost;
if(i == V - 1) return true;//如果第n次仍然更新了,则存在负圈
}
}
}
return false;
}