单源最短路径 Bellman_Ford

来源:互联网 发布:淘宝友邦电器城假货 编辑:程序博客网 时间:2024/05/21 20:26

一、问题简述
Dijkstra算法无法判断含负权边的图的最短路。如果遇到负权,在没有负权回路(回路的权值和为负,即便有负权的边)存在时,也可以采用Bellman - Ford算法正确求出最短路径。
Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=(V,E), 其源点为v0。
接下来分析回路问题,如果有正回路,则动态规划会因为松弛操作选出最优答案,必然是不会走重复回路的,那么n-1个节点就能找到再也无法松弛的结果,无须担心。但是如果是负回路,那么步数越多,则距离越小,所以对最后结果再进行松弛操作,也是可以找到答案的。
如果不存在这样的负回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。
附:松弛操作
这里写图片描述
二、算法思想
首先:初始化邻接矩阵
其次:进行不断的松弛操作(bellman函数)
这里写图片描述

另外:再把每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果(minus函数)。
最后:path函数去寻找路径

三、具体代码

#include <iostream>#include<stack>#include<stdio.h>#include<stdlib.h>#include<iostream>#include<queue>#include<climits>#include<cstring>using namespace std;struct node0{    int kdist;    int dist;    int path;};int i,j,k;int bellman( int **graph,node0 *node,int v0,int n)   //v0表示源顶点 {    for(int j=0;j<n;j++)     //初始化 完成     {        node[j].kdist=graph[v0][j];        node[j].dist=graph[v0][j];        node[j].path=v0;     //path记录最短路径上从v0到i的前一个顶点         if(graph[v0][j]==INT_MAX)            node[j].path=-1;    }    //完成表格 node[n-1][0到n-1],也即所有节点,在最多经过n-1个其它节点之后,最短路径问题     for(int k=2;k<n;k++)         {        //接下来目的是完成:任意一个节点u,在最多经过k个其它节点之后的最短路径为node[k][u]; 并且接下来把node[k-1][i]称为i离源点的现有最短距离。 i是u的相邻节点         for(int u=0;u<n;u++)        {               int temp=INT_MAX;int tempfrom=-1;             for(int i=0;i<n;i++)            {                       if(node[i].kdist!=INT_MAX&&graph[i][u]!=INT_MAX&&i!=u                    &&temp>node[i].kdist+graph[i][u])                  //目前这个关联节点的现有最短距离不能是无穷远,                 //这两个节点是相邻节点,距离不是无穷远                  //可以松弛操作:(相邻节点i自身到源点的现有距离和ui之间的距离之和、要比u到源点的现有距离远)                           {                    temp=node[i].kdist+graph[i][u];                    tempfrom=i;//存下来i当作前向节点                 }            }            node[u].path=tempfrom;// 记住最后完成松弛操作的前向节点             int tempkdist=node[u].kdist;            node[u].kdist=node[u].dist;            node[u].dist=min(tempkdist,temp) ;        }    }}int minus0(int **graph,node0 *node,int n)//判断是否有负回路。现在经过了n-1个点得到最短路径node[u].dist,那么如果有负回路,则再经过别的点,可以让路径更短,对最后结果进行一次松弛操作判断 {    for(int u=0;u<n;u++)        {               int temp=INT_MAX;int tempfrom=-1;             for(int i=0;i<n;i++)            {                       if(node[i].dist!=INT_MAX&&graph[i][u]!=INT_MAX&&i!=u                 &&temp>node[i].dist+graph[i][u])                        {                    temp=node[i].dist+graph[i][u];                    tempfrom=i;//存下来i当作前向节点                 }            }            if(temp<node[u].dist)//注意是dist。               { return -1;}        }     return 0;}void showPath(node0 *node,int i,int v0,int n)   //打印最短路径上的各个顶点 {    int *store=(int *)malloc(sizeof(int)*n);    int count=0;    while(i!=v0)    {        store[count++]=i;        i=node[i].path; //node[n-1][u].path里面存着的都是最后也即最好的让它完成松弛操作的前向节点     }    store[count]=v0;    for(int j=count;j>=0;j--)        cout<<store[j]<<ends;    free(store);} void input(int **graph,int n,int edge)//完成邻接矩阵{    int i,j;    int from,to,weight;        int v0;    for(i=0;i<n;i++)    {        for(j=0;j<n;j++)        {            if(i!=j)                graph[i][j]=INT_MAX;        }        graph[i][i]=0;    }    cout<<"请输入from,to,weight"<<endl;     for (int i = 0; i < edge; i++)    {        fin>>from>>to>>weight;        graph[from][to]=weight;     } }int main(){    int n,edge,v0;     //表示输入的顶点数和边数     cout<<"请输入节点和边数"<<endl;     cin>>n>>edge;    cout<<"请输入源点"<<endl;     fin>>v0;    int **graph;    graph=new int *[n+1];//n行,n列。     for(int i=0;i<n;i++)       graph[i]=new int [n+1];    input(graph,n,edge);       node0 *node;    node=new node0[n+1];//num+1行,all+1列。     int lookme;    bellman(graph,node,v0,n);    lookme=minus0(graph,node,n);     if(lookme==-1)        cout<<"警告!警告!存在负环路!系统即将关闭"<<endl;     else for(int i=0;i<n;i++)    {        if(i!=v0)       {                if(node[i].dist!=INT_MAX)                {                    cout<<i<<"的路径是";                    showPath(node,i,v0,n);                    cout<<endl<<"   距离为"<<node[i].dist<<endl;                }                else cout<<i<<"无法到达"<<endl;       }    }}

四、实验结果
5 10 0
0 1 6
0 3 7
1 2 5
1 4 -4
1 3 8
3 2 -3
3 4 9
2 1 -2
4 3 7
4 0 2

这里写图片描述

0 0
原创粉丝点击