题解 P1931 【套利】
Created_equal1 · · 题解
把汇率表抽象成一个有向图,每个货币是一个结点,每条边上的权值就是汇率。
无法套利的情况分两种
1、没有环,即最后无法回到该种货币
2、有环,但是得到的利率不大于1
我们可以跑一遍单源最长路,用SPFA来判环,只是一般的松弛条件中的加号需要改成乘号。
可以证明如果环上所有边的权值之积大于1,那么一定可以用SPFA判定出有向图中存在环
(
#include <map>
#include <deque>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const size_t Max_N(35);
const size_t Max_M(10000);
size_t Adj[Max_M], Next[Max_M], Head[Max_N];
double Weight[Max_M];
double Dist[Max_N];
bool In_queue[Max_N];
unsigned int Times[Max_N];
unsigned int n, m;
unsigned int fu;
bool spfa(const size_t &S)
{
memset(Dist, 0, sizeof(Dist));
memset(In_queue, 0, sizeof(In_queue));
memset(Times, 0, sizeof(Times));
std::deque<size_t> Q;
Dist[S] = 1.0;
In_queue[S] = true;
Q.push_back(S);
Times[S] = 1;
while (!Q.empty())
{
size_t top = Q.front();
In_queue[top] = false;
Q.pop_front();
for (int i = Head[top];i != 0;i = Next[i])
if (Dist[Adj[i]] < Dist[top] * Weight[i])
{
Dist[Adj[i]] = Dist[top] * Weight[i];
if (!In_queue[Adj[i]])
{
In_queue[Adj[i]] = true;
Q.push_back(Adj[i]);
++Times[Adj[i]];
if (Times[Adj[i]] >= n)
return true;
}
}
}
return false;
}
int main()
{
while (cin >> n, n)
{
unsigned int tot = 0;
++fu;
memset(Adj, 0, sizeof(Adj));
memset(Next, 0, sizeof(Next));
memset(Head, 0, sizeof(Head));
memset(Weight, 0, sizeof(Weight));
map<string, unsigned int> v;
string b, e;
double number;
for (unsigned int i = 1;i <= n;++i)
v[(cin >> b), b] = i;
cin >> m;
while (m--)
{
++tot;
cin >> b >> number >> e;
Adj[tot] = v[e];
Weight[tot] = number;
Next[tot] = Head[v[b]];
Head[v[b]] = tot;
}
for (unsigned int i = 1;i <= n;++i)
if (spfa(i))
{
printf("Case %u: Yes\n", fu);
goto loop;
}
printf("Case %u: No\n", fu);
loop:
;
}
return 0;
}
)