单源最短路径 Bellman_Ford

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

另外:再把每次松弛时把每条边都更新一下,若在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

《单源最短路径 Bellman_Ford》

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/Ye_wanxuan/article/details/54630581
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞