图的最短路径算法 Bellman-Ford算法
Bellman-Ford算法由美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明。
适用范围:
- 有向图,无向图(需把edge重复2遍);
即对于边w(u, v),存储2遍: w(u,v), w(v,u);
- 适用于从图中某个固定点,到其他点的最短路径求解;
算法大致说明:
1. 初始化所有点,用dis[i]数组存储root点到i点的最短路径;
2. dis[root]为0,其他的dis[i]用最大值初始化;
3. 遍历所有的边w(u,v);如果dis[u]+w(uv)的值小于当前的dis[v],则替换之;
4. pre[i] 保存访问的路径;
#include<iostream> #include<cstdio> using namespace std; #define MAX 0x3f3f3f3f #define N 1010 int nodenum, edgenum, original; //点,边,起点 typedef struct Edge //边 { int u, v; int cost; }Edge; Edge edge[N]; int dis[N], pre[N]; bool Bellman_Ford() { for (int i = 1; i <= nodenum; ++i) //初始化 dis[i] = (i == original ? 0 : MAX); for (int i = 1; i <= nodenum - 1; ++i) for (int j = 1; j <= edgenum * 2; ++j) { if (dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~) { dis[edge[j].v] = dis[edge[j].u] + edge[j].cost; pre[edge[j].v] = edge[j].u; } } bool flag = 1; //判断是否含有负权回路 for (int i = 1; i <= edgenum*2; ++i) if (dis[edge[i].v] > dis[edge[i].u] + edge[i].cost) { flag = 0; break; } return flag; } void print_path(int root) //打印最短路的路径(反向) { while (root != pre[root]) //前驱 { printf("%d-->", root); root = pre[root]; } if (root == pre[root]) printf("%d\n", root); } int main() { scanf("%d%d%d", &nodenum, &edgenum, &original); pre[original] = original; for (int i = 1; i <= edgenum; ++i) { scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost); edge[edgenum + i].u = edge[i].v; edge[edgenum + i].v = edge[i].u; edge[edgenum + i].cost = edge[i].cost; } if (Bellman_Ford()) for (int i = 1; i <= nodenum; ++i) //每个点最短路 { printf("path from %d to %d is: %d\n", original, i, dis[i]); printf("Path:"); print_path(i); } else printf("have negative circle\n"); system("pause"); return 0; }
一个测试用例:
6 8 6 6 5 100 6 4 30 6 2 10 4 5 60 4 3 20 3 5 10 2 3 50 1 2 5