zoukankan      html  css  js  c++  java
  • 带负权图的单源最短路径算法:Bellman-Ford算法

    算法简介   

           前面介绍过图的单源最短路径算法Dijkstra算法,然而Dijkstra算法无法判断含负权边的图的最短路。如果遇到负权,在没有负权回路存在时(负权回路的含义是,回路的权值和为负。)即便有负权的边,也可以采用Bellman-Ford算法正确求出最短路径。
            Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数 w是 边集 E 的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。

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

    适用条件&范围

    • 单源最短路径(从源点s到其它所有顶点v);
    • 有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);
    • 边权可正可负(如有负权回路输出错误提示);
    • 差分约束系统;

    算法流程

    1. 初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
    2. 迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
    3. 检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
    参考代码实现:
    此处为有向图的实现,要验证,只需在main()函数下运行test_BellmanFord()函数即可。
    #include<iostream>
    using namespace std;
    const int  MaxEdges= 100;
    const int MaxVertex= 20;
    const int  MaxWeight= 10000;
    
    
    struct MyEdge
    {
    	int v1,v2;
    	int weight;
    	MyEdge()
    	{
    		v1 = -1;
    		v2 = -1;
    		weight = MaxWeight;
    	}
    };
    struct MyGraph
    {
    	MyEdge edges[MaxEdges];	
    	int NumVertex;
    	int NumEdges;	
    };
    
    
    void createGraph(MyGraph *mg)
    {
    
    	cout<<"请输入顶点数和边数:"<<endl;
    	cin>>mg->NumVertex >>mg->NumEdges;
    	cout<<"请依次输入各边权重(v1,v2,weight):"<<endl;
    	int v1,v2 ,weight;
    	for (int j=0;j<mg->NumEdges;j++)
    	{
    		cin>>v1>>v2>>weight;
    		mg->edges[j].v1=v1;
    		mg->edges[j].v2=v2;
    		mg->edges[j].weight = weight;	
    	}	
    }
    
    
    bool Bellman_ford(MyGraph *mg,int s,int *dis,int *pre)
    {	
    	int numV = mg->NumVertex;
    	
    	for(int i = 0;i<numV;i++) //初始化距离
    	{
    		dis[i] = (i==s)? 0:MaxWeight;
    		pre[i] = -1; //前一个点初始为-1
    	}
    	pre[s]=s;
    	for(int i=1;i<numV;i++)//松弛操作
    	{
    		for(int j=0;j<mg->NumEdges;j++)
    		{
    			if(dis[mg->edges[j].v2] > dis[mg->edges[j].v1] + mg->edges[j].weight)
    			{
    				dis[mg->edges[j].v2] = dis[mg->edges[j].v1] + mg->edges[j].weight;
    				pre[mg->edges[j].v2] = mg->edges[j].v1;
    			}
    		}
    	}
    	for(int j=0;j<mg->NumEdges;j++)//判断是否含有负权回路
    	{
    		if(dis[mg->edges[j].v2] > dis[mg->edges[j].v1] + mg->edges[j].weight)
    			return false;
    	}
    	return true;
    }
    
    void print_path( int j,int *pre)
    {
    	while(pre[j]!=-1 && pre[j]!= j)
    	{
    		cout<<j<<"-->";
    		j=pre[j];
    	}
    	if(j ==pre[j])
    		cout<<j;
    	
    }
    
    void test_BellmanFord()
    {
    	int dis[MaxVertex];//
    	int pre[MaxVertex];
    	MyGraph MG;
    	createGraph(&MG);
    	
    	cout<<"输入Bellman_ford算法的起点:";
    	int start;
    	cin>>start;
    	if(Bellman_ford(&MG,start,dis,pre))
    	{
    		for(int j=0;j<MG.NumVertex;j++)
    		{
    			cout<<"到点"<<j <<"的最短路径:"<<dis[j]<<endl;
    			cout<<"路径:";
    			print_path(j,pre);
    			cout<<endl;
    		}
    	}
    	else
    		cout<<"存在负权环,算法无法实行!"<<endl;
    }
    

    运行结果样例:


  • 相关阅读:
    bzoj 4012: [HNOI2015]开店
    POJ 1054 The Troublesome Frog
    POJ 3171 Cleaning Shifts
    POJ 3411 Paid Roads
    POJ 3045 Cow Acrobats
    POJ 1742 Coins
    POJ 3181 Dollar Dayz
    POJ 3040 Allowance
    POJ 3666 Making the Grade
    洛谷 P3657 [USACO17FEB]Why Did the Cow Cross the Road II P
  • 原文地址:https://www.cnblogs.com/f8master/p/3826062.html
Copyright © 2011-2022 走看看