POJ 2240 - Arbitrage(bellman_ford & floyd)

题意:

给出一些货币和货币之间的兑换比率,问是否可以使某种货币经过一些列兑换之后,货币值增加。

举例说就是1美元经过一些兑换之后,超过1美元。可以输出Yes,否则输出No。

 

分析:

首先我们要把货币之间的关系转化成一张图。转化时,用STL里面的map很方便。

为每种货币分配一个序列号,一个序列号代表了一个图中间的NODE,而node之间的edge用汇率表示。

 

一开始用Dijkstra算法做,死活AC不了,网友给的理由是:

由于Dijkstra算法不能处理带有负权值的最短路,但此题中,两种货币之间的兑换比率可能小于1,相当于这条路径的权值为负

前车之鉴,WA的代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<string.h>
#include<string>
#include<map>
using namespace std;
#define maxn 31
#define inf 0x3f3f3f3f
double edges[maxn][maxn];
double dist[maxn];
typedef pair<int,int> P;
void init(){
	for(int i=1;i<maxn;i++)
		for(int j=1;j<maxn;j++)
			edges[i][j]=(i==j?1:0);//1代表等价价换,0:代表无法交换
}

void dijkstra(int s,int n){
	bool visited[maxn];
	memset(visited,0,sizeof(visited));
	visited[s]=true;
	for(int i=1;i<=n;i++)
		dist[i]=edges[s][i];
	for(int i=1,u;i<=n;i++){
		double max=-1;
		for(int j=1;j<=n;j++)
			if(!visited[j]&&dist[j]>max){
				max=dist[j]; u=j;
			}

			visited[u]=true;
			for(int j=1;j<=n;j++){
				if(dist[u]*edges[u][j]>dist[j])
					dist[j]=dist[u]*edges[u][j];
			}
	}
}

int main(){
	//freopen("in.txt","r",stdin);
	int n,cases=0;
	while(scanf("%d",&n)!=EOF && n){
		cases++;
		init();
		map<string,int> ma;
		string s,t;
		for(int i=1;i<=n;i++){
			cin>>s;
			ma.insert(make_pair(s,i));
		}
		int m;
		double tmp;
		scanf("%d",&m);
		while(m--){
			cin>>s>>tmp>>t;
			edges[ma[s]][ma[t]]=tmp;
		}

		dijkstra(1,n);
		printf("Case %d: ",cases);
		if(dist[1]>1.0)
			printf("Yes\n");
		else 
			printf("No\n");
	}
}

 

最后写了一个Floyd的代码版本,结果一下子就过了,我只能默默的哭了

#include<iostream>
 #include<cstdio>
 #include<queue>
 #include<string.h>
 #include<string>
 #include<map>
 using namespace std;
 #define maxn 31
 #define inf 0x3f3f3f3f
 double edges[maxn][maxn];
 void init(){
     for(int i=1;i<maxn;i++)
         for(int j=1;j<maxn;j++)
             edges[i][j]=(i==j?1:0);//1:代表等价交换,0:代表无法交换
 }
 
 void floyd_warshall(int s,int n){
     for(int k=1;k<=n;k++){
         for(int i=1,u;i<=n;i++){
             for(int j=1;j<=n;j++){
                 if(edges[i][k]*edges[k][j]>edges[i][j])//如果找到了更好的交换方案
                     edges[i][j]=edges[i][k]*edges[k][j];
             }
         }
     }
 }
 
 int main(){
     //freopen("in.txt","r",stdin);
     int n,cases=0;
     while(scanf("%d",&n)!=EOF && n){
         cases++;
         init();
         map<string,int> ma;
         string s,t;
         for(int i=1;i<=n;i++){
             cin>>s;
             ma.insert(make_pair(s,i));
         }
         int m;
         double tmp;
         scanf("%d",&m);
         while(m--){
             cin>>s>>tmp>>t;
             edges[ma[s]][ma[t]]=tmp;
         }
 
         dijkstra(1,n);
         printf("Case %d: ",cases);
         /*for(int i=1,u;i<=n;i++){
             for(int j=1;j<=n;j++){
                 printf("%.2f  ",edges[i][j]);
             }
             printf("\n");
         }*/
         if(edges[1][1]>1.0)
             printf("Yes\n");
         else 
             printf("No\n");
     }
}

 

最后再用bellman_ford模板成功ac了一次。

 #include<iostream>
 #include<cstdio>
 #include<queue>
 #include<string.h>
 #include<string>
 #include<map>
 using namespace std;
 
 #define maxn 1000
 #define inf 0x3f3f3f3f    
 struct edge{
     int from;
     int to;
     float cost;
     edge(){
         from=0,to=0,cost=0;
     }
     edge(int a,int b ,float c){
         from=a,to=b,cost=c;
     }
 };
 edge Edges[maxn];
 float dist[maxn];
 void bellman_ford(int s,int V,int E){
     for(int i=1;i<=V;i++)
         dist[i]=0;
     dist[s]=1.0;
     for(int i=1;i<=V;i++){//这里可以改成while(true),但在有负权的情况下,会增加更多的循环,故建议照样例中写
         bool update=false;
         for(int j=1;j<=E;j++){
             edge e=Edges[j];
             if(dist[e.to]<dist[e.from]*e.cost){
                 dist[e.to]=dist[e.from]*e.cost;
                 update=true;
             }
         }
         if(!update)break;//一旦在某次循环中,不能再更新当前dist,那么就能提前结束该算法,break之
     }
 }
 
 void init(int n){
     for(int i=1;i<=n;i++){
         for(int j=1;j<=n;j++){
             if(i==j)   Edges[i]=edge(i,j,1.0);//初始化,1:等价交换
             else       Edges[i]=edge(i,j,0);  //0:无法交换
         }
     }
 }
 
 
 int main(){
     //freopen("in.txt","r",stdin);
     int n,cases=0;
     while(scanf("%d",&n)!=EOF && n){
         cases++;
         init(n);
         map<string,int> ma;
         string s,t;
         for(int i=1;i<=n;i++){
             cin>>s;
             ma.insert(make_pair(s,i));   //构造字符串与数值的映射关系
         }
         int m;
         double tmp;
         scanf("%d",&m);
         for(int i=1;i<=m;i++){
             cin>>s>>tmp>>t;
             Edges[i]=edge(ma[s],ma[t],tmp);
         }
 
         bellman_ford(1,n,m);
         printf("Case %d: ",cases);
         if(dist[1]>1.0)
             printf("Yes\n");
         else 
             printf("No\n");
     }
 }

 

【问题】:Folyd算法能处理包含负权边的图吗?

       答案是可以的。Floyd-Warshall算法和Bellman-Ford算法一样,可以处理边是负数的情况。

                                                                                                           ---《挑战程序设计竞赛》 P.103

posted @ 2014-08-24 15:38  姜楠  阅读(228)  评论(0)    收藏  举报