转载注明出处csdnbestsort
Bellman – ford算法是求含负权图的单源最短路径的一种算法,效率较低(O(nm)),代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成.
为什么是松弛n-1次?简单来说就是从源点到一个点的最短路最极限的一种情况的路径需要经过全部的点,也就是需要松弛v-1次,这样,我们执行v-1次就可以保证所有的点都松弛到最佳的情况,如果执行了v-1次后还能继续松弛,那就说明图中有负权环,无解.
bellman的松弛操作有点类似于Dijkstra算法,是对u->v以及u->k->v(k为中间值)取最小值,n-1次松弛后,dis数组中储存的即为要求点到各个点之间的最小值.
主代码如下:
const int maxn = 1e4;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
int dis[maxn];
struct edge{
int s,e; ///起点,终点
int w; ///权值
}e[maxn];
int n,m; //n为点,m为边的总数
bool bellman(int a,int n) ///求a->其他点的最短路,n为结点总数.可判负环
{
memset(dis,inf,(n+1)<<2); //将数组前n+1个数初始化为INF
dis[a]=0;
For(i,n-1) ///for(int i=0;i<n-1;i++),下同
For(j,m)
dis[e[j].e]=min(dis[e[j].e],dis[e[j].s]+e[j].w); ///松弛操作,即上文的u->v和u->k->v
For(i,m) ///松弛完后还能再松弛即代表有负环
if(dis[e[i].e]>dis[e[i].s]+e[i].w)
return true;
return false;
}
对于一张图,将边点信息存入邻接矩阵后,直接调用bellma(a,n)即可,a为要求的点,n为点的总数
调用后dis[i]即为a->的最短路
如果函数返回true,则代表有负环
完整代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define mem(a,b) memset(a,b,sizeof(a))
#define For(a,b) for(int a=0;a<b;a++)
using namespace std;
const int maxn = 1e4;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
int dis[maxn];
struct edge{
int s,e; ///起点,终点
int w; ///权值
}e[maxn];
int n,m; //n为点,m为边的总数
bool bellman(int a,int n) ///求a->其他点的最短路,n为结点总数.可判负环
{
memset(dis,inf,(n+1)<<2);
dis[a]=0;
For(i,n-1)
For(j,m)
dis[e[j].e]=min(dis[e[j].e],dis[e[j].s]+e[j].w); ///松弛操作
For(i,m) ///松弛完后还能再松弛即代表有负环
if(dis[e[i].e]>dis[e[i].s]+e[i].w)
return true;
return false;
}
int main()
{
cin>>n>>m;
For(i,m)
cin>>e[i].s>>e[i].e>>e[i].w;
if(bellman(1,n))
cout << "有负环" << endl;
else
for(int i=1;i<=n;i++){
if(dis[i]!=INF)
cout<< dis[i] << endl;
else
cout <<"INF" << endl;
}
}