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;
    }
    

    运行结果样例:


  • 相关阅读:
    WPF 自适应布局控件
    c# 将Datarow转成Datarowview
    C# 全局Hook在xp上不回调
    WPF datagrid AutoGenerateColumns隐藏部分列
    WPF wpf中按钮操作权限控制
    C# autofac配置文件中设置单例
    Castle ActiveRecord 二级缓存使用 异常记录
    VS2013 抛出 stackoverflow exception 的追踪
    CastleActiveRecord在多线程 事务提交时数据库资源竞争导致更新失败的测试结果记录
    WF4.0 工作流设计器 传入参数问题记录?
  • 原文地址:https://www.cnblogs.com/f8master/p/3826062.html
Copyright © 2011-2022 走看看