之前有介绍了单元最短路算法迪杰斯特拉算法和弗洛伊德算法。但是前者没有办法处理负环。后者时间复杂度太高所以都不是最优的所以在这里介绍另一种算法。
Bellman-Ford算法:这个是利用动态规划的思想。反复理由已有的边来更新最短路。如果dist【v】满足dist【v】 <= dist【u】 + map【u】【v】。那么更新dist【v】的值。反复利用上述式子来更新dist内的值,那什么时候结束呢?最多重复n-1次,因为对于一个最短路来说,最长的边也不过是n-1条边。
实现过程:
s为源,map【】【】为地图,map【u】【v】为点u到v的边的长度,结果存在dist数组中。
1) 初始化,所有点i赋初值dist【i】 = INF 出发点为s,dist【s】 = 0;
2)对于每条边,如果dist【u】 != INF 并且 dist【v】 > dist【u】 + map【u】【v】,则赋值。
3)循环2)n-1次。或知道某次不在更新。
核心代码:
for(int i = 0 ; i < n - 1; i ++)
for(int j = 0 ; j < m ; j++)
if(dist[u] != INF && dist[v] > dist[u] + map[u][v]){
dist[v] = dist[u] + map[u][v];
}
当然了既然这个算法有处理负环的能力,那么我们可以用这个算法来判断时候有负环。
大致思路是,如果有负环那么在n次进行一遍松弛操作,dist值发生改变,就有负环。
把上述代码微调一下,如下:
for(int j = 0 ; j < m ; j++)
if(dist[u] != INF && dist[v] > dist[u] + map[u][v]){
return false;
}
return true;
上述是邻接矩阵的写法。下面我用链式前向星的方法写一下模板(判断负环)。不懂链式前向星怎么用的小伙伴可以参考一下我之前的博客。
bool bellmanFord(int s,int head[maxn],NODE edge[maxn],int dist[maxn]){
int i,j,k;
for(i = 0 ; i < n ; i ++) // 初始化
dist[i] = INF;
dist[s] = 0;
for(int i = 0 ; i < n - 1; i ++){ //循环n-1次
for(j = 0 ; j < n ; j ++){ //链式前向星的遍历边的方法
if(dist[j] == INF) continue; //若dist【j】为INF表明s没有办法直接到点j。就没有办法通过以j为起点的边来松弛其他点。
for(k = head[j] ; k != -1 ; k = edge[k].next){
if(edge[k].w != INF && dist[edge[k].to] > dist[j] + edge[k].w ){
dist[edge[k].to] = dist[j] + edge[k].w;
}
}
}
}
for(j = 0 ; j < n ; j ++){
if(dist[j] == INF)
continue;
for(k = head[j];k!=-1;k = edge[k].next){
if(edge[k].w != INF && dist[edge[k].to] > dist[j] + edge[k].w ){
return false;
}
}
}
return true;
}