题意:
已知n种货币,以及m种货币汇率及方式,问能否通过货币转换,使得财富增加。
题目链接:Arbitrage
解题思路:
目测是一条不错的生财之道~~~(打住)
财富增加的话肯定是以同种货币作比较,否则没有意义,因此题意大致可以变成在一个n个顶点的图中能否找到一个正权环,这里的正权环指财富增加,很明显可用Bellman-ford 算法(专门解决存在环的最短路径问题)。
Bellman-ford 算法:一个具有n个顶点的图如果不存在环,则从顶点x,到顶点y,最多经过n-1条边(要考虑连通性,每个顶点最多经过 1 次),因此 x 到 y 的最短路 最多经过 n – 1 次松弛操作(就是更新长度)就应该出现,如果第 n 次松弛还可以得到最优,那么这个图就肯定是存在环了(直接用Dijkstra 就无法得到最优的,环的存在会影响最优解是否存在)。
这里给的顶点时货币的英文名,为了方便简洁,用map 将货币名 与 编号一一对应
此题有多种解法,注意到顶点最大为30(限制很小,不愧是模板练习题),可用Floyd算法求出整个图的最短路,注意此处并不是真正的最短路,但通过插点,即 i -> j 能否改成 i -> k -> j 的路,就会把环给考虑至少一次,那么对应的 path[i][i] 也就是本金肯定是增加的(相对于初值),为了方便,将本金设为 1 .
代码;
Bellman_ford 实现:
#include<iostream>
#include<cstdio>
#include<queue>
#include<map>
#include<string>
using namespace std;
#define Max 900
int n, m, k;
double disit[Max];
struct P {
int s;
int e;
double t;
}dp[Max];
bool B_floyd(int u)
{
memset(disit, 0, sizeof(disit));
disit[u] = 1;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < k; j++)
{
if (disit[dp[j].e] < (disit[dp[j].s] * dp[j].t))
{
disit[dp[j].e] = disit[dp[j].s] * dp[j].t;
}
}
}
for (int i = 0; i < k; i++)
{
if (disit[dp[i].e] < (disit[dp[i].s] * dp[i].t))
{
return true;
}
}
return false;
}
int main()
{
int nn = 1;
string strs, strs1;
double rab;
while (~scanf("%d", &n) && n != 0)
{
k = 0;
memset(disit, 0, sizeof(disit));
map<string, int> mp;
cin.get();
for (int i = 0; i < n; i++)
{
cin >> strs;
mp[strs] = i;
}
scanf("%d", &m);
cin.get();
for (int i = 0; i < m; i++)
{
cin >> strs >> rab >> strs1;
dp[k].e = mp[strs1];
dp[k].s = mp[strs];
dp[k++].t = rab;
}
int flag = 0;
for (int i = 0; i < n; i++)
{
if (B_floyd(i))
{
cout <<"Case "<<nn++<< ": Yes" << endl;
flag = 1;
break;
}
}
if (!flag)
cout<<"Case "<<nn++<< ": No" << endl;
cout << endl;
}
return 0;
}
另一个代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<map>
#include<string>
using namespace std;
#define Max 100
int n, m;
int visit[Max];
double disit[Max];
double dp[Max][Max];
bool B_floyd(int u)
{
queue<int>q;
disit[u] = 1;
q.push(u);
while (!q.empty())
{
int temp = q.front();
visit[temp] = 0;
q.pop();
for (int i = 0; i < n; i++)
{
if (dp[temp][i] != 0)
{
if (disit[i] < (disit[temp] * dp[temp][i]))
{
disit[i] = (disit[temp] * dp[temp][i]);
if (disit[u] > 1)
{
return true;
}
if (!visit[i])
{
visit[i] = 1;
q.push(i);
}
}
}
}
}
return false;
}
int main()
{
string strs, strs1;
int nn = 1;
double rab;
while (~scanf("%d", &n) && n != 0)
{
map<string, int> mp;
cin.get();
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++)
{
cin >> strs;
mp[strs] = i;
}
scanf("%d", &m);
cin.get();
for (int i = 0; i < m; i++)
{
cin >> strs >> rab >> strs1;
dp[mp[strs]][mp[strs1]] = rab;
}
int flag = 0;
for (int i = 0; i < n; i++)
{
memset(visit, 0, sizeof(visit));
memset(disit, 0, sizeof(disit));
if (B_floyd(i))
{
cout << "Case " << nn++ << ": Yes" << endl << endl;
flag = 1;
break;
}
}
if(!flag)
cout << "Case " << nn++ << ": No" << endl << endl;
}
return 0;
}