poj2240——Arbitrage

题目大意:给出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;  
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值