贝尔曼(bellmanford)算法:
大意:分为两步:1,对各边进行松弛操作,即更新最短距离 2,判断是否产生回路
Bellman-Ford算法的流程如下:
给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,
数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为, Distant[s]为0;
以下操作循环执行至多n-1次,n为顶点数:
对于每一条边e(u, v),如果Distant[u] + w(u, v)< Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v)< Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。
可知, Bellman-Ford算法 寻找单源最短路径的时间复杂度为O(V*E).
例子:
Description
While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ's farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1..N, M (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.
As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself :) .
To help FJ find out whether this is possible or not, he will supply you with complete maps to F (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.
Input
Line 1: A single integer, F. F farm descriptions follow.
Line 1 of each farm: Three space-separated integers respectively: N, M, and W
Lines 2..M+1 of each farm: Three space-separated numbers (S, E, T) that describe, respectively: a bidirectional path between S and E that requires T seconds to traverse. Two fields might be connected by more than one path.
Lines M+2..M+W+1 of each farm: Three space-separated numbers (S, E, T) that describe, respectively: A one way path from S to E that also moves the traveler back T seconds.
Output
Lines 1..F: For each farm, output "YES" if FJ can achieve his goal, otherwise output "NO" (do not include the quotes).
Sample Input
2 3 3 1 1 2 2 1 3 4 2 3 1 3 1 3 3 2 1 1 2 3 2 3 4 3 1 8
Sample Output
NO YES
题目大意:
FJ童鞋家的农田里面有若干个虫洞,有一些虫洞是相连的,可以消耗一些时间爬过去(无向)。还有一些虫洞比较特殊,从一头爬到那一头之后时间会退后一些(有向)
FJ希望通过爬虫洞(?)来时时间退后,从而能看到另外一个自己。根据输入的虫洞信息来判断是否可以实现FJ的愿望。
code:
#include<stdio.h>
#define inf 999999
struct node
{
int u,v,w;
} edge[6000];
int dis[505];
int n,m,w,in;
void add(int u,int v,int c)
{
in++;
edge[in].u=u;
edge[in].v=v;
edge[in].w=c;
}
int bellman()
{
int u,v,w,i,j,flag;
for(i=1; i<=n; i++)//初始化
{
dis[i]=inf;
}
dis[1]=0;
flag=0;
for(i=1; i<=n; i++)//边的松弛
{
for(j=1; j<=in; 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(i=1; i<=in; i++)//判断是否有回路
if(dis[edge[i].v]>dis[edge[i].u]+edge[i].w)
return 1;
return 0;
}
int main()
{
int i,j,t,u,v,c;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&w);
in=0;
for(i=0; i<m; i++)
{
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,c);
}
for(i=0; i<w; i++)
{
scanf("%d%d%d",&u,&v,&c);
add(u,v,-1*c);
}
if(bellman())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
Dijkstra算法和Bellman算法思想有很大的区别:
Dijkstra算法在求解过程中,源点到集合S内各顶点的最短路径一旦求出,则之后不变了,修改的仅仅是源点到T集合中各顶点的最短路径长度。Bellman算法在求解过程中,每次循环都要修改所有顶点的dist[],也就是说源点到各顶点最短路径长度一直要到Bellman算法结束才确定下来。
如果存在从源点可达的负权值回路,则最短路径不存在,因为可以重复走这个回路,使得路径无穷小。
作为一种单源最短路径算法,Bellman-Ford对于有向图和无向图都能适用,它还有一个Dijkstra算法无法具备的特点,那就是对含负权图的最短路径搜索。
每i轮对边的遍历之后,只要不存在负权回路,Bellman-Ford算法都可以保证获得距离源点i条边的点的最短路径。因为最短路径的最大长度不会超过n条边,n为节点的数目。所以n次遍历后所有的节点必定都能找到最短路径。如果n次后还可以继续松弛,则表明该图存在负权回路,可以对代码稍作修改来计算负权环的大小。