http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2894
谈一下对贝尔曼福特的认识(参考别人的)
BF是对边进行操作,dijkstra 是对点进行操作,N个顶点的最短路最多是N-1条边,所以需要循环N-1次
1.初始化
2.迭代求解:反复对边集中的每条边进行松弛操作,使得顶点集v中的每个顶点vde最短距离逐步逼近其最短距离,运行v-1次
3:检验负权回路:判断边集中的每一条边的两个端点是否收敛,如果存在未瘦脸的顶点,则返回false,表明问题无解,否则算法返回true,并且从源点可达的顶点v的最短距离保存在的d【v】中。
4描述性证明编辑
Bellman-Ford算法可以大致分为三个部分
第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
d(v) > d (u) + w(u,v)
则返回false,表示途中存在从源点可达的权为负的回路。
之所以需要第三部分的原因,是因为,如果存在从源点可达的权为负的回路。则 应为无法收敛而导致不能求出最短路径。
考虑如下的图:
经过第一次遍历后,点B的值变为5,点C的值变为8,这时,注意权重为-10的边,这条边的存在,导致点A的值变为-2。(8+ -10=-2)
第二次遍历后,点B的值变为3,点C变为6,点A变为-4。正是因为有一条负边在回路中,导致每次遍历后,各个点的值不断变小。
在回过来看一下bellman-ford算法的第三部分,遍历所有边,检查是否存在d(v) > d (u) + w(u,v)。因为第二部分循环的次数是定长的,所以如果存在无法收敛的情况,则肯定能够在第三部分中检查出来。比如
此时,点A的值为-2,点B的值为5,边AB的权重为5,5 > -2 + 5. 检查出来这条边没有收敛。
所以,Bellman-Ford算法可以解决图中有权为负数的边的单源最短路径问。
http://blog.csdn.net/u012860063/article/details/24492003
// BF 贝尔曼福特代码模板,学习 #include <stdio.h> #include <string.h> #define INF 999999 struct node { int u,v,w; }q[4000002]; int dis[500002]; int t = 0; int n,m; int s,e; int u,v,w; int flag; void add(int u,int v,int w) { q[t].u = u; q[t].v = v; q[t++].w = w; } void BF() { int i,j; for(i = 0;i<=n;i++) { dis[i] = INF; } dis[s] = 0; for(i = 1;i<=n-1;i++) { flag = 0; for(j = 0;j<t;j++) { if(dis[q[j].v]>dis[q[j].u] + q[j].w) { dis[q[j].v] = dis[q[j].u] + q[j].w; flag = 1; } } if(flag == 0) break; } printf("%d ",dis[e]); } int main() { int i = 0; while(scanf("%d%d",&n,&m)!=EOF) { for(i = 0;i<m;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } scanf("%d%d",&s,&e); BF(); } return 0; }