1.算法描述
Bellman-Ford算法是由RichardBellman和Lester Ford所提出的,用于求解单源最短路径。与Dijkstra算法所不同的是,边的权值可为负,算法复杂度O(VE)。
dis[i]记录源点到点i的最短距离,V表示顶点个数,E表示边的条数。
算法具体步骤如下:
(1)对每一条边(u,v)进行松弛操作:dis[v]=min{dis[v], dis[u]+edge[u][v]} ,每一次的松弛操作是对相邻节点的访问;
(2)循环重复松弛操作V-1次;
(3)检查负权环:对边(u,v),如果存在dis[v]>dis[u]+edge[u][v],则负权环存在。
优化:如果dis[]没有更新,则后续的循环重复中也不会更新,因而只要dis[]无更新则退出循环。
2.Referrence
[1] Wikipedia,http://zh.wikipedia.org/zh-cn/贝尔曼-福特算法
[2] Tanky Woo,最短路径算法—Bellman-Ford(贝尔曼-福特)算法分析与实现(C/C++)
3.问题
3.1 POJ 3259
题目大意:有F个farm,每个farm由N个field组成;field之间有双向的path,也有单向并且是负权的wormhole;现在求解是否存在负权环。
再次栽在标号处理的问题:数组dis[ ]的标号与每个field的标号相差1。
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 5200
#define inf 0xffff
typedef struct
{
int start,end;
int weight;
}EDGE;
EDGE path [MAX];
int num_path;
void init(int M,int W)
{
int i,st,en,wei;
num_path=0;
for(i=0;i<M;i++)
{
scanf("%d%d%d",&st,&en,&wei);
path[num_path].start=st;
path[num_path].end=en;
path[num_path].weight=wei;
num_path++;
path[num_path].start=en;
path[num_path].end=st;
path[num_path].weight=wei;
num_path++;
}
for(i=0;i<W;i++)
{
scanf("%d%d%d",&st,&en,&wei);
path[num_path].start=st;
path[num_path].end=en;
path[num_path].weight=-wei;
num_path++;
}
}
int bellman_ford(int N)
{
int i,j,negative_flag=0,relax_flag;
int *dis=(int*) malloc((N+1)*sizeof(int));
/*initialize dis*/
for(i=1;i<N;i++)
dis[i]=inf;
dis[1]=0;
/*relax*/
for(j=1;j<N;j++)
{
relax_flag=0;
for(i=0;i<num_path;i++)
if(dis[path[i].start]+path[i].weight<dis[path[i].end])
{
dis[path[i].end]=dis[path[i].start]+path[i].weight;
relax_flag=1;
}
if(!relax_flag)
break;
}
/*check negative loop*/
for(i=0;i<num_path;i++)
if(dis[path[i].start]+path[i].weight<dis[path[i].end])
{
negative_flag=1;
break;
}
free(dis);
return negative_flag;
}
int main()
{
int num_farm,N,M,W;
scanf("%d",&num_farm);
while(num_farm--)
{
scanf("%d%d%d",&N,&M,&W);
init(M,W);
if(bellman_ford(N))
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
3.2 POJ 2240
货币套现的问题,可以简化为:
(1)将每一种货币看作一个顶点,交易比率(exchange rate)作为一条有向边,建立有向图;
(2)dis[i]表示到源点source到节点i的最大交易比率,修改松弛条件:dis[v]=max{dis[v], dis[u]*rate(u,v)} ;
(3)判断套现(arbitrage)是否可能,即判断dis[source]>1.0 。
边的数组开小了,贡献两次Runtime Error;输出少打了一个空格,贡献了一次Presentation Error 。
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 30
#define MAXM 1000
typedef struct
{
int start,end;
double weight;
}EDGE;
EDGE edge[MAXM];
char currencies[MAXN][20];
int N,M,arbitrage_flag;
int map(char str[20],int N)
{
int i;
for(i=0;i<N;i++)
if(!strcmp(str,currencies[i]))
return i;
return -1;
}
void init()
{
int i;
char str1[20],str2[20];
double rate;
for(i=0;i<N;i++)
scanf("%s",currencies[i]);
scanf("%d",&M);
for(i=0;i<M;i++)
{
scanf("%s%lf%s",str1,&rate,str2);
edge[i].start=map(str1,N);
edge[i].end=map(str2,N);
edge[i].weight=rate;
}
}
void bellman_ford(int source)
{
int i,j;
double *dis=(double*) malloc(N*sizeof(double));
arbitrage_flag=0;
/*initialize dis*/
memset(dis,0,N*sizeof(double));
dis[source]=1.0;
/*relax*/
for(j=1;j<=N;j++)
{
for(i=0;i<M;i++)
if(dis[edge[i].start]*edge[i].weight>dis[edge[i].end])
dis[edge[i].end]=dis[edge[i].start]*edge[i].weight;
}
/*check whether arbitrage is possible*/
if(dis[source]>1.0)
arbitrage_flag=1;
free(dis);
}
int main()
{
int i,test_case=1;
while(scanf("%d",&N)!=EOF&&N)
{
init();
for(i=0;i<N;i++)
{
bellman_ford(i);
if(arbitrage_flag)
break;
}
if(arbitrage_flag)
printf("%s%d%s\n","Case ",test_case,": Yes");
else
printf("%s%d%s\n","Case ",test_case,": No");
test_case++;
}
return 0;
}
3.3 POJ 1860
与POJ 2240不同的是,要减去commission。
判断negative sum:dis[v] < (dis[u]-commission(u,v))*rate(u,v) 。
数组point开了100,贡献了一次Runtime Error,将MAX改成200就AC了。
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 200
typedef struct
{
int start,end;
double rate,commission;
}EXCHANGE;
EXCHANGE point[MAX];
int N,M,S;
double V;
void init()
{
int i,a,b;
scanf("%d%d%d%lf",&N,&M,&S,&V);
for(i=0;i<M;i++)
{
scanf("%d%d",&a,&b);
point[i].start=a-1;
point[i].end=b-1;
scanf("%lf%lf",&point[i].rate,&point[i].commission);
point[M+i].start=b-1;
point[M+i].end=a-1;
scanf("%lf%lf",&point[M+i].rate,&point[M+i].commission);
}
}
/*return larger number*/
double larger(double be,double af)
{
return be>=af?be:af;
}
int bellman_ford( )
{
int i,j,negative_flag=0;
double *dis=(double*) malloc(N*sizeof(double));
/*initialize dis*/
memset(dis,0,N*sizeof(double));
dis[S-1]=V;
/*relax*/
for(j=1;j<=N;j++)
for(i=0;i<2*M;i++)
dis[point[i].end]=larger(dis[point[i].end],(dis[point[i].start]-point[i].commission)*point[i].rate);
/*check negative loop*/
for(i=0;i<2*M;i++)
if(dis[point[i].end]<(dis[point[i].start]-point[i].commission)*point[i].rate)
{
negative_flag=1;
break;
}
free(dis);
return negative_flag;
}
int main()
{
init();
if(bellman_ford())
printf("YES\n");
else
printf("NO\n");
return 0;
}