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 <iostream>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <string>

using namespace std;
#define MAXN 50

double dis[MAXN][MAXN];//存放匯率
map<string, int> 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<string, int>(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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章