Bellman-ford算法

来源:互联网 发布:电子产品外壳设计软件 编辑:程序博客网 时间:2024/05/23 11:38

权值的概念引入:

权值就是定义的路径上面的值。
一般来说,权值愈小,路径愈佳。

Bellman-ford算法的简介:

Bellman - ford算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。

松弛:

松弛就是更新两点间的最短路径。


Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=(V,E), 其源点为s,加权函数 w是 边集 E 的映射。对图G运行Bellman - Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。

  1. 初始化:将除源点外的所有顶点的最短距离估计值 d[v] ——>+∞, d[s]——>0;
  2. 迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
  3. 检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
    ##注意:
    无论是在算法导论上,还是,实际的代码比较过程中,bellman算法和dijisktra算法都非常像,但是有几个关键的不同导致了两个算法的根本不同。在松弛的过程中,dijiskstra算法中有着贪心的思路,它每一次迭代都找出了一个子问题的最短路径。并用到新的一轮迭代的过程中去,使问题的规模不断减少,因此需要用到一个visit数组来记录该节点的最短路径是否被找到。另外,在dijisktra算法中默认,路径的权值都为正数。而bellman算法中,很明显,并不是贪心的。首先它没有visit数组来记录节点的最短路径是否被找到,其次,它每次迭代的规模是一样大的,并不能减少迭代的规模。,其次,因为,在bellman算法中设置了一个检查循环,因此,用bellman算法能检查出是否存在负权环。
    ##代码如下:
#include<iostream>#include<cstdio>using namespace std;#define MAX 0x3f3f3f3f#define N 1010int 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)//进行nodenum-1次的松弛遍历        for(int j = 1; j <= edgenum; ++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; ++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);    }//u,v,起点终点。cost路径长度    if(Bellman_Ford())//        for(int i = 1; i <= nodenum; ++i) //每个点最短路        {            printf("%d\n", dis[i]);            printf("Path:");            print_path(i);        }    else        printf("have negative circle\n");    return 0;}

PS:终于差不多理解了。QAQ

原创粉丝点击