EOJ1270-Arbitrage(套利交易)

题目大意:有很多种外币,它们之间部分可以互相兑换,假如你有一个单位的某种货币,问是否可以通过有限次兑换回到当初你拥有的那种货币,使得最后得到的货币多于一个单位。例如:1美元换0.5英镑,1英镑换10法币,1法币换0.21美元。从1美元出发,兑换过程为:1×0.5×10×0.21=1.05>1,所以该套利交易可行。

ps:以上汇率仅作说明,不代表市场上真正的汇率。


题目分析:按题目意思可以抽象成一个带权有向图求“最长路径”,每次从一个点出发,经过一个环路再回到出发点,实现一次套利交易,所以检查每个点的所有环,直到找到一次可行的套利交易或者全部检查完都没有可行的套利交易。从这个想法出发去找所以的环实现起来很麻烦,所以把问题简化,可以想到,这道题类似求给定节点对的最短路径,那么我们可以借助Floyd算法来实现这一过程,不过需要做一点小小的改动。


//dis[i][j] 表示货币i到货币j的汇率
void init()
{
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
		{
			if(i == j)
				dis[i][j] = 1.0;//与自身的汇率为1.0
			else
				dis[i][j] = 0.0;//当前i不可交换j
		}
}

对dis数组进行初始化,如果两种货币不能交换,则置dis[i][j]=0.0 相当于最短路径里面置为无穷大,表示i不可到达j。


void Folyd()
{
	for(int k = 1; k <= n; k++)
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
			{
				if(dis[i][j] < dis[i][k]*dis[k][j])//"松弛操作"
					dis[i][j] = dis[i][k]*dis[k][j];
			}

}
Floyd核心代码,注意松弛操作的过程,如果货币i先兑换到货币k,再由货币k兑换到货币j得到的货币更多,那么就进行松弛操作,即dis[i][j] = dis[i][k]*dis[k][j];

完整代码如下:

#include 
#include 
#include 
#include 
#include 

using namespace std;
#define MAXN 50

double dis[MAXN][MAXN];//存放汇率
map money;//索引货币顶点
int n, m;//n个顶点、m条边

void Folyd()
{
	for(int k = 1; k <= n; k++)
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
			{
				if(dis[i][j] < dis[i][k]*dis[k][j])//"松弛操作"
					dis[i][j] = dis[i][k]*dis[k][j];
			}

}

void init()
{
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
		{
			if(i == j)
				dis[i][j] = 1.0;//与自身的汇率为1.0
			else
				dis[i][j] = 0.0;//当前i不可交换j
		}
}

int main(int argc, char const *argv[])
{	
	int u, v, count=1;
	int i, flag;
	double rate;
	string s, str;
	while(scanf("%d", &n) && n != 0)
	{
		flag = 0;//标记
		money.clear();//清空
		init();//dis数组初始化
		for(i = 1; i <= n; i++)
		{
			cin>>s;
			money.insert(pair(s, i));//货币名与顶点配对
		}
		scanf("%d", &m);
		while(m--)
		{
			cin>>s>>rate>>str;
			u = money[s];
			v = money[str];
			dis[u][v] = rate;//u到v汇率为rate
		}
		Folyd();
		for(i = 1; i <= n; i++)
			if(dis[i][i] > 1.0)//有可行的套利交易
			{
				flag = 1;
				break;
			}		
		if(flag == 1)
			printf("Case %d: Yes\n", count++);
		else
			printf("Case %d: No\n", count++);
	}	
	return 0;
}


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

对于dis的对角线元素的判断还可以放在Floyd函数中:

void Folyd()
{
	for(int k = 1; k <= n; k++)
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
			{
				if(dis[i][j] < dis[i][k]*dis[k][j])//"松弛操作"
					dis[i][j] = dis[i][k]*dis[k][j];
				if(i == j && dis[i][j] > 1.0)
				{
					flag = 1;//定义为全局变量
					return ;
				}
			}
}



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

测试数据:

输入:

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

输出:

Case 1: Yes
Case 2: No 


Folyd算法参考:http://blog.csdn.net/niushuai666/article/details/6772706


你可能感兴趣的:(EOJ)