Bellmen算法是求最短路径的最方便的算法之一,SPFA算法更方便,是Bellmen算法的队列实现但队列不会,掌握Bellmen算法即可解决大部分最短路径的问题。Bellmen算法的最大优势就在于可以解决边权值为负的情况。(限于有向图)
另外,Bellmen算法的运算时间为O(nm)适合n^2>m的稀疏图!!!当遇到n^2<m的稠密图时最好用Dijkstra算法,其运算时间为O(n^2)。
典例:给n个点,m条路径,求A点到B点的最短路径。
需要开设dis[]记录,dis[i]即到达i点的最短距离,输出时输出dis[i]即可。
*核心代码:
for(int i=1;i<=n-1;i++)
{
for(int j=1;j<=m;j++)
{
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].w)
dis[edge[j].v]=dis[edge[j].u]+edge[j].w;
}
}
理论上一共要进行n-1轮松弛,但有可能最后几轮的松弛将不再改变dis[]中的值,故可以提前结束循环节省运算时间。需要一个与dis[]一样大的数组dis1[]备份dis[]的值,另外还引进一个flag变量初始化为0。每次松弛过后将dis[]与dis1[]中的值一一比对,若相同则flag=1;判断if(flag==1)则结束循环无需再松弛。
#include <iostream>
#include<stdio.h>
#define max 99999999
using namespace std;
int dis[200],dis1[200],n,m,a,b;
struct node
{
int u,v,w; //起点。重点,权值
}edge[100];
void init() //初始化
{
for(int i=1;i<=n;i++)
dis[i]=max; //初始化将到达每个点的距离设为无穷
dis[a]=0; //起点开始故距离为0
}
int main()
{
while(cin>>n>>m>>a>>b)
{
init();
for(int i=1;i<=m;i++)
{
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
//bellmen核心语句!!!
int flag;
for(int i=1;i<=n-1;i++)
{
flag=0;
for(int j=1;j<=m;j++)
{
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].w)
dis[edge[j].v]=dis[edge[j].u]+edge[j].w;
}
for(int j=1;j<=n;j++)
if(dis[j]!=dis1[j])
{
flag=1;dis1[j]=dis[j];
}
if(flag==0) break; //若值均未变则跳出循环
}
//输出结果
cout<<dis[b]<<endl;
}
return 0;
}
数据:
Input:5 5 1 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
Output:4
运算过程(理解运算原理)
初始化:dis 1 2 3 4 5
0 max max max max
第一轮:先处理第一条边,2——>3距离为2,此时dis[2]和dis[3]均为max,无法比较,松弛失败。第二条边1——>2距离为-3,此时dis[1]=0,dis[2]=max,dis[2]>dis[1]+(-3),故dis[2]值变为-3。第三条边1——>5距离为5,dis[5]>dis[1]+5,故dis[5]=5。以此方法,第三四条边无法松弛,故第一轮松弛结束。
dis 1 2 3 4 5
0 -3 max max 5
第二轮:处理刚才未松弛成功的第一条边2——>3,dis[3]>dis[2]+(-3),dis[3]=-1。之后3——>4,dis[4]>dis[3]+3,dis[4]=2。发现了从4——5距离会比1——>5短,但松弛3——>4时已经过了4——>5的边,只能再进行一轮松弛。
dis 1 2 3 4 5
0 -3 -1 2 5
第三轮:处理4——>5,dis[5]>dis[4]+2,dis[5]=4。
dis 1 2 3 4 5
0 -3 -1 2 4
第四轮:无变化的值,flag=0,跳出循环。
dis 1 2 3 4 5
0 -3 -1 2 4