带负权图的单源最短路径算法: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;
}

运行结果样例:

带负权图的单源最短路径算法:Bellman-Ford算法,布布扣,bubuko.com

时间: 2024-12-27 20:01:07

带负权图的单源最短路径算法:Bellman-Ford算法的相关文章

图论(四)------非负权有向图的单源最短路径问题,Dijkstra算法

Dijkstra算法解决了有向图G=(V,E)上带权的单源最短路径问题,但要求所有边的权值非负. Dijkstra算法是贪婪算法的一个很好的例子.设置一顶点集合S,从源点s到集合中的顶点的最终最短路径的权值均已确定.算法反复选择具有最短路径估计的顶点u,并将u加入到S中,对u 的所有出边进行松弛.如果可以经过u来改进到顶点v的最短路径的话,就对顶点v的估计值进行更新. 如上图,u为源点,顶点全加入到优先队列中. ,队列中最小值为u(值为0),u出队列,对u的出边进行松弛(x.v.w),队列最小值

图的单源最短路径:Dijkstra算法实现

本文介绍的是图的非负权值的单源最短路径问题.问题的提出是,对于有权图D,t提供源点v,要找到从v到其他所有点的最短路径,即单源最短路径问题,在本文中,解决这一问题,是普遍比较熟悉的Dijkstra算法. 算法核心思想参见维基.简而言之,设集合S存放已经求出了最短路径的点.初始状态S中只有一个点v0,之后每求得v0到vn的最短路径,就会更新v0到所有vn邻接的点的一致的最短路径(不一定是最终的最短路径),如此重复,每次会确定v0到一个点的最短路径,确定好的点加入S中,直至所有点进入S结束.在本文中

用小根堆实现dijkstra,求图的单源最短路径

小根堆实现dijkstra 求图的最短路径,最常用的有四种方法: 1.Floyed(弗洛伊德)算法.最简单的最短路径算法,可以求多源最短路径.时间复杂度为O(n*n*n). 2.Dijkstra(迪杰斯特拉)算法.只能求单源最短路径.时间复杂度为O(n*n). 3.Bellman-Ford(贝尔曼福德)算法.只能求单源最短路径.时间复杂度为O(NE)(N为顶点数,E是边数). 4.SPFA算法.为Bellman-Ford算法的队列实现,减少不必要的冗杂计算.只能求单源最短路径.时间复杂度为O(k

算法导论--单源最短路径问题(Dijkstra算法)

转载请注明出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51918844 单源最短路径是指:给定源顶点s∈V到分别到其他顶点v∈V?{s}的最短路径的问题. Dijkstra算法采用贪心策略:按路径长度递增的顺序,逐个产生各顶点的最短路径.算法过程中需要维护一个顶点集S,此顶点集保存已经找到最短路径的顶点.还需要维护一个距离数组dist, dist[i]表示第i个顶点与源结点s的距离长度. Dijkstra算法思路: S

单源最短路径Dijkstra和优先级算法

百度百科:迪杰斯特拉算法. 代码实现如下: import java.util.Comparator; import java.util.PriorityQueue; import java.util.Scanner; class Node { int x; int value; } public class Main { static int N,M,cnt; static final int MAX = 105; static final int INF = 1000009; static N

单源最短路径Dijkstra、BellmanFord、SPFA【模板】

Dijkstra算法: 将所有点分为两个集合.如果源点s到u的最短路径已经确定,点u就属于集合Va,否则属于集合Vb. 1.将源点s到图中各点的直接距离当做初始值记录为s到各点的最短距离,不能到达的记为INF.S到S距离为0. 2.在集合Vb中的点中找一个点u,使得源点s到该点u路径长度最短,将u从Vb中除去,加到V1中.这时候求出了当前S到u的最短路径. 3.把新确定的点u更新s到集合Vb中每一个点v的距离,如果s到u的距离加上u到v的直接距离小于当前s到v的距离,则表示新找到的最短路径长度比

luogu P3371 &amp; P4779 ---单源最短路径spfa &amp; 最大堆优化Dijkstra

P3371 [模板]单源最短路径(弱化版) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包含三个整数N.M.S,分别表示点的个数.有向边的个数.出发点的编号. 接下来M行每行包含三个整数Fi.Gi.Wi,分别表示第i条有向边的出发点.目标点和长度. 输出格式: 一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i

数据结构:单源最短路径--Dijkstra算法

Dijkstra算法 单源最短路径 给定一带权图,图中每条边的权值是非负的,代表着两顶点之间的距离.指定图中的一顶点为源点,找出源点到其它顶点的最短路径和其长度的问题,即是单源最短路径问题. Dijkstra算法 求解单源最短路径问题的常用方法是Dijkstra(迪杰斯特拉)算法.该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点. 算法思想 带权图G=<V,E>,令S为已确定了最短路径顶点的集合,则可用V-S表示剩余未确定最短路径顶点的集合.假设V0是源点,则初始 S={V

Dijkstra单源最短路径

Dijkstra单源最短路径 给定一个带权有向图G=(V,E) ,其中每条边的权是一个非负实数.另外,还给定 V 中的一个顶点,称为源.现在我们要计算从源到所有其他各顶点的最短路径长度.这里的长度是指路上各边权之和.这个问题通常称为单源最短路径问题. 下面给出两个计算单源最短路径的模板. Dijkstra_简化版:时间复杂度O(n^2),不可处理重边图 //计算图的以s点为起点的单源最短路径 //图中节点从1到n编号 //运行dijkstrea之前,需要先把图中两点间的距离保存在dist[i][