题目大意:给出n种货币的转换汇率,问能不能通过买卖货币赚钱(比如1US=0.5英镑 1英镑 = 10法郎 1法郎= 0.21US 最后能赚0.05US)
输入:n(1<=n<=30)
第i种货币的名字(共n行)
汇率转换表的行数m
源货币名字 汇率 目标货币名字(共m行)
输出:Case i: Yes/No
分析:多源最长路径。
可以用floyd算法变形,相当于求出每种货币到自身一个环的最长路径,最后遍历所有种类的货币,看看有没有环汇率大于1的,如果有就是能赚钱。也可以用bellman-ford算法。边权有负值时不能用dijkstra算法。
代码:
方法一:floyd变形。转载自https://www.cnblogs.com/Tree-dream/p/5732966.html
#include <stdio.h>
#include <string.h>
#include <map>
#include <string>
#include <iostream>
using namespace std;
map< string , int > s;
double graph[ 40 ][ 40 ];
int main()
{
// freopen("in.txt","r",stdin);
int n,p = 0;
while( cin>>n,n )
{
p++;
memset( graph , 0 , sizeof( graph ) );
string a , b;
double m;
int x,mark = 0;
for(int i = 1 ; i <= n ; i++ )
{
cin>>a;
s[ a ] = i; //对货币和数字建立一个映射。
}
cin>>x;
for( int i = 1 ; i <= x ; i++ )
{
cin>>a>>m>>b;
graph[ s[ a ] ][ s[ b ] ] = m;
}
for( int k = 1 ; k <= n ; k++ ) //floyd求出两个货币间的最大汇率。
for( int i = 1 ; i <= n ; i++ )
for( int j = 1 ; j <= n ; j++ )
if( graph[ i ][ j ] < graph [ i ][ k ]*graph[ k ][ j ])
graph[ i ][ j ] = graph [ i ][ k ]*graph[ k ][ j ];
for(int i = 1 ; i <= n ; i++ ) //判断。
if( graph[ i ][ i ] > 1 ) mark = 1;
if( mark ) printf("Case %d: Yes\n",p);
else printf("Case %d: No\n",p);
}
return 0;
}
方法二:Bellman-ford算法 转载自https://blog.csdn.net/sinat_34263473/article/details/52155096
//Bellman_ford
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
int n,m;
double dist[40]; //注意类型为 double
struct edge //边的结构体,st为起点,ed为终点,rt为汇率
{
int st,ed;
double rt;
edge(int sst,int eed,double rtt) : st(sst),ed(eed),rt(rtt) {}
edge() {}
};
vector<edge> G;
map<string ,int> mp;
bool Bellman_ford(int v)
{
memset(dist,0,sizeof(dist));
dist[v] = 1;
for(int j = 1;j < n;j++) { // n - 1 次松弛操作
for(int i = 0;i < G.size();i++) {
int p1 = G[i].st,p2 = G[i].ed;
if(dist[p2] < dist[p1] * G[i].rt) { //尝试更新顶点 v 到 p2 的距离
dist[p2] = dist[p1] * G[i].rt;
}
}
}
for(int i = 0;i < G.size();i++){
int p1 = G[i].st,p2 = G[i].ed;
if(dist[p2] < dist[p1] * G[i].rt) { //第 n 次松弛可以得到更优解,则存在环
return true;
}
}
return false;
}
int main()
{
int cas = 1;
string s,ss;
while(~scanf("%d",&n) &&n) {
for(int i = 0;i < n;i++) {
cin >> s;
mp[s] = i; //货币名 s 的顶点编号 为 i
}
cin >> m;
string beg,ends;
double r;
G.clear();
for(int i = 0;i < m;i++) {
cin >> beg >> r >> ends; //边的信息读取
G.push_back(edge (mp[beg] ,mp[ends] ,r) );
}
printf("Case %d: ",cas++);
for(int i = 0;i < n;i++) { //枚举所有开始的起点
if(Bellman_ford(i)) { //存在环
cout << "Yes" << endl;
//printf("bellman %d\n",i);
break;
}
else if(i == n - 1)
cout << "No" <<endl;
}
}
return 0;
}
方法三:spfa。转载自http://blog.csdn.net/seasonjoe/article/details/51130134
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
using namespace std;
int n, m;
char name[100][100];
double map[100][100],dis[100];
bool spfa(int x)
{
queue<int > que;
bool vis[100];
int count[100];
memset(vis, false, sizeof(vis));
memset(dis, 0, sizeof(dis));
memset(count, 0, sizeof(count));
dis[x] = 100.0;
vis[x] = true;
que.push(x);
count[x]++;
while (!que.empty())
{
int temp = que.front(); que.pop();
vis[temp] = false;
for (int i = 0; i < n;i++)
if (dis[temp] * map[temp][i] > dis[i])
{
dis[i] = map[temp][i] * dis[temp];//松弛操作,有的点已经在队列中就不用入队
if (!vis[i]) //如果当前点不在队列中
{
que.push(i);
count[i]++;
vis[i] = true;
if (count[i] >= n)//如果入队次数大于等于n即有正权回路
return true;
}
}
}
return false;
}
int main()
{
int num = 1;
int x, y,i,j;
while (~scanf("%d", &n), n)
{
memset(map, 0, sizeof(map));
for (i = 0; i < n; i++)
scanf("%s", name[i]);
scanf("%d", &m);
char temp1[100], temp2[100];
double rate;
while(m--)
{
scanf("%s %lf %s", temp1, &rate, temp2);
for (j = 0; j < n;j++)
if (strcmp(name[j], temp1) == 0)
{
x = j;
break;
}
for (j = 0; j < n; j++)
if (strcmp(name[j], temp2) == 0)
{
y = j;
break;
}
map[x][y] = rate;
}
bool flag = true;
printf("Case %d: ", num++);
for (i = 0; i < n;i++) //每个点都找是否存在正权回路
if (spfa(i))
{
flag = false;
break;
}
if (!flag)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}