http://poj.org/problem?id=2240
题目大意:输入一些币种,输入这些币种的汇率,求是否存在套汇(套汇及利用汇率之间的差异,从而将某一单位的币种,兑换回多于一单位的同种货币)。
思路:建立图论模型,每种币种为顶点,两种币种间的汇率为一条有向边,构造好有向网后,问题转化为判断图中是否存在某个顶点,从它出发的某条回路权值乘积大于1,大于1则存在套汇。具体求解时,可用Bellman-ford算法求从源点出发到各个顶点v(包括源点自己)。
Bellman-ford算法实现原理为:用一个dist数组记录最短路径,共更新k次(1<k<n),共n-1次,代表当前点到源点经过k条边,求最短路径时取最小值,依次更新。用邻接矩正实现时时间复杂度为n^3.
//以求最短路径为例,允许存在负权值边,但不允许负权值回路
//初始化
for (i=0;i<n;i++) //n代表顶点个数
dist[i]=edge[v0][i]; //v0代表源点,此时表示源点经过一条边到达各个顶点V的距离长度
//一下共循环n-2更新最短路径
for (k=2;k<n;k++)
{
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
if (edge[i][j]!=INF&&edge[i][j]+dist[i]<dist[j]) //如i到j有边相连,并且此路劲更短,则更新。
{
dist[j]=dist[j]+edge[i][j];
}
}
}
}
此题只需将bellman-ford稍加变形便可实现,因套汇必形成回路,形成回路最多有n条边,及只需要将最长路径更新n次即可判断是否存在套汇。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 900
#define INF 0xfffffff
char s[MAX][MAX];
typedef struct node
{
int u,v;
float w;
}node;
node map[MAX];
float dist[MAX];
int n,m;
int Max(int x,int y)
{
return x>y?x:y;
}
int bellman_ford(int v0)
{
int i,j,k;
memset(dist,0,sizeof(dist));
dist[v0]=1;
for (k=0;k<n;k++) //形成回路,最多有n条边,循环n次。
{
for (i=0;i<m;i++)
{
if (dist[map[i].v]<dist[map[i].u]*map[i].w)
{
dist[map[i].v]=dist[map[i].u]*map[i].w;
}
}
}
if (dist[v0]>1) //若源点币种大于1,则使用该币种兑换可以实现套汇。
return 1;
return 0;
}
int main()
{
int tot=0;
while (~scanf("%d",&n))
{
if (n==0)
break;
int i,j,u,v,flag=0;
char sa[MAX],sb[MAX];
float ans;
for (i=0;i<n;i++)
{
scanf("%s",s[i]);
}
scanf("%d",&m);
for (i=0;i<m;i++)
{
getchar();
scanf("%s %f %s",sa,&ans,sb);
for (j=0;j<n;j++)
{
if (strcmp(sa,s[j])==0)
u=j;
if (strcmp(sb,s[j])==0)
v=j;
}
map[i].u=u;
map[i].v=v;
map[i].w=ans;
}
for (i=0;i<n;i++)
{
if (bellman_ford(i)) //存在套汇则不需要继续往下判断。
{
flag=1;
break;
}
}
printf("Case %d: ",++tot);
if (flag)
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
return 0;
}