题意:
给出一些货币和货币之间的兑换比率,问是否可以使某种货币经过一些列兑换之后,货币值增加。
举例说就是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 "); else printf("No "); } }
最后写了一个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(" "); }*/ if(edges[1][1]>1.0) printf("Yes "); else printf("No "); } }
最后再用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 "); else printf("No "); } }
【问题】:Folyd算法能处理包含负权边的图吗?
答案是可以的。Floyd-Warshall算法和Bellman-Ford算法一样,可以处理边是负数的情况。
---《挑战程序设计竞赛》 P.103