P1931 套利-SPFA最长路与环的判断

题目连接套利 - 洛谷

制作不易,点赞走人

首先,每种货币看做一个点,map哈希赋值赋成若干个数字,货币之间转换关系看做两个节点之间的有向,注意是有向边,权值为比率,最长路的值就代表了兑换到该节点时的比率。比如当前root点,如果套利了,那就是root通过最长路回到了root,由于root最初比率是1,最长路还能回到root说明现在比率一定大于1,也就实现了套利。

细节很多,多次输入,多次初始化链式前向星数组以及map数组;

spfa函数内也有很多初始化要注意,还有就是因为我们不知道图是否联通对每一个点进行Spfa即可;

# include<iostream>
# include<stack>
# include<cstring>
# include<math.h>
# include<map>
# include<queue>
# include<iomanip>
using namespace std;
typedef long long int ll;

struct node
{
    double w;
    int id;

};
typedef struct
{
    int b,e;
    double w;
} xinxi;
xinxi s[1000];
int len, f[1000],nex[1000];

map<string,int>mm;
double w[1000];
bool book[1000];
queue<node>q;
void add(int x,int y,double z)
{
    s[len].b=x;
    s[len].w=z;
    s[len].e=y;
    nex[len]=f[x];
    f[x]=len;
    len++;
}
int n,cnt1[50];
bool spfa(int root)
{
    memset(book,0,sizeof(book));
    memset(w,0,sizeof(w));
    memset(cnt1,0,sizeof(cnt1));

    while(!q.empty())
    {
        q.pop();
    }

    struct node head;

    head.id=root;
    head.w=1;
    q.push(head);

    book[root]=1;
    w[root]=1;
    cnt1[root]=1;

    while(!q.empty())
    {
        struct node now=q.front();

        q.pop();

        int id=now.id;

        book[id]=0;
        int x=f[id];


        while(x!=-1)
        {
            int j=s[x].e;


            if(w[j]<w[id]*s[x].w)
            {
                w[j]=w[id]*s[x].w;

                if(!book[j])
                {
                    struct node head;

                    head.id=j;

                    head.w=w[j];

                    q.push(head);

                    cnt1[j]++;
                    if(cnt1[j]>n)
                        return 1;

                    book[j]=1;
                }
            }

            x=nex[x];
        }


    }

    return  0;


}
int main( )
{

    memset(f,-1,sizeof(f));


    int timecnt=0;
    while(cin>>n&&n)
    {
        memset(f,-1,sizeof(f));

        memset(nex,0,sizeof(nex));

        len=0;

        mm.clear();

        timecnt++;

        int cnt=1;

        int nn=n;

        while(nn--)
        {
            string t;
            cin>>t;

            mm[t]=cnt;

            cnt++;
        }

        int m;

        cin>>m;
        getchar();
        while(m--)
        {
            string qian,hou;
            double p;

            cin>>qian>>p>>hou;

            add(mm[qian],mm[hou],p);
          

        }

        int flag=0;

        for(int i=1; i<cnt; i++)
        {
            if(spfa(i))
            {
                flag=1;
                cout<<"Case "<<timecnt<<':'<<" "<<"Yes"<<'\n';
                break;
            }
        }
        if(!flag)
        {
            cout<<"Case "<<timecnt<<':'<<" "<<"No"<<'\n';
        }

    }


    return 0;
}

题目描述

套利是利用汇率差异实现货币增值。例如,1美元可以兑换0.5英镑、1英镑可以兑换10法郎、1法郎可以兑换0.21美元。接下来,一个聪明的交易商就可以从1美元开始,0.5 * 10.0 * 0.21 =1.05美元,获得了5%的利润。

你的任务是写一个程序,从输入文件读入汇率清单,然后决定套利是有可能的或没有可能的。

输入格式

输入文件包含多组数据,每组数据的第一行是一个整数n( 1<=n<=30 )。代表有多少种货币。接下来n行字符串,每行表示一种货币的名称(名称中不会出现空格)。下一行是一个整数m,约定了汇率表的长度。随后的m行中,每行有三部分组成。The last m lines each contain the name ci of a source currency, a real number rij which represents the exchange rate from ci to cj and a name cj of the destination currency. Exchanges which do not appear in the table are impossible. 每组数据之间空一行。当n=0时表示输入数据结束。

输出格式

第i组数据,输出Case i: 后,如果可以套利,输出Yes,否则输出No。

输入输出样例

输入 #1复制

3
USDollar
BritishPound
FrenchFranc
3
USDollar 0.5 BritishPound
BritishPound 10.0 FrenchFranc
FrenchFranc 0.21 USDollar

3
USDollar
BritishPound
FrenchFranc
6
USDollar 0.5 BritishPound
USDollar 4.9 FrenchFranc
BritishPound 10.0 FrenchFranc
BritishPound 1.99 USDollar
FrenchFranc 0.09 BritishPound
FrenchFranc 0.19 USDollar

0

输出 #1复制

Case 1: Yes
Case 2: No
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qinsanma and Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值