Arbitrage POJ - 2240
Arbitrage is the use of discrepancies in currency exchange rates to transform one unit of a currency into more than one unit of the same currency. For example, suppose that 1 US Dollar buys 0.5 British pound, 1 British pound buys 10.0 French francs, and 1 French franc buys 0.21 US dollar. Then, by converting currencies, a clever trader can start with 1 US dollar and buy 0.5 * 10.0 * 0.21 = 1.05 US dollars, making a profit of 5 percent.
Your job is to write a program that takes a list of currency exchange rates as input and then determines whether arbitrage is possible or not.
Input The input will contain one or more test cases. Om the first line of each test case there is an integer n (1<=n<=30), representing the number of different currencies. The next n lines each contain the name of one currency. Within a name no spaces will appear. The next line contains one integer m, representing the length of the table to follow. 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.
Test cases are separated from each other by a blank line. Input is terminated by a value of zero (0) for n. Output For each test case, print one line telling whether arbitrage is possible or not in the format "Case case: Yes" respectively "Case case: No". Sample Input
3USDollarBritishPoundFrenchFranc3USDollar 0.5 BritishPoundBritishPound 10.0 FrenchFrancFrenchFranc 0.21 USDollar3USDollarBritishPoundFrenchFranc6USDollar 0.5 BritishPoundUSDollar 4.9 FrenchFrancBritishPound 10.0 FrenchFrancBritishPound 1.99 USDollarFrenchFranc 0.09 BritishPoundFrenchFranc 0.19 USDollar0
Sample Output
Case 1: Yes
Case 2: No
題意:
錢幣套匯問題,就是利用貨幣之間的匯率差異,從而將一單位的某種貨幣,兌換回多於一單位的同種貨幣。題目每組資料給出N種貨幣,M種匯率關係,每種關係是每單位的前者兌換後者的匯率(單向)。問通過這些匯率兌換會不會出現套匯現象。
解題思路:
其實每種貨幣可以看成一個點,貨幣之間的匯率看成這兩點構成的邊的權值,構造好圖之後就是求從一點出發,看經過幾條路徑之後可以得到比自身大的值(但是注意路徑上的權值是相乘的關係不是相加)。
Floyd演算法:
因為最多30種貨幣,所以用Flayd演算法也可以,Floyd演算法的思想也比較好理解,主要就是圖的構造,(看代碼注釋)需要注意的是,題目要求最大匯率關係,所以更新資料時是求大於本身的,並且權值為相乘關係,具體看代碼注釋。
AC代碼:
#include<stdio.h>#include<string.h>char huo[36][36];char aa[36], bb[36];double mp[36][36], x;int main(){ int n, g = 1; while( scanf("%d",&n) && n ) { for( int i = 1; i <= n; i++ ) scanf("%s",huo[i]); //存入貨幣種類 int m; //初始化圖,因為沒有匯率關係的 memset(mp,0,sizeof(mp)); //貨幣之間不能兌換,賦值為0 for( int i = 1; i <= n; i++ ) mp[i][i] = 1; //同種貨幣之間匯率為1 scanf("%d",&m); for( int i = 1; i <= m; i++ ) { int x1, x2; scanf("%s%lf%s",aa,&x,bb); for( int j = 1; j <= n; j++ ) { if( strcmp(huo[j],aa) == 0 ) //尋找是第幾種貨幣,轉化為點 { x1 = j; break; } } for( int j = 1; j <= n; j++ ) { if( strcmp(huo[j],bb) == 0 ) //尋找是第幾種貨幣,轉化為點 { x2 = j; break; } } mp[x1][x2] = x; //賦權值 } for( int k = 1; k <= n; k++ ) { for( int i = 1; i <= n; i++ ) { for( int j = 1; j <= n; j++ ) { if( mp[i][j] < mp[i][k] * mp[k][j] ) //因為找最大匯率關係,所以為“<”和“*” mp[i][j] = mp[i][k] * mp[k][j]; } } } int flag = 0; for( int i = 1; i <= n; i++ ) { if( mp[i][i] > 1 ) //發現有超過本身的值,說明有套匯現象 { flag = 1; break; } } printf("Case %d: ",g++); if( flag ) printf("Yes\n"); else printf("No\n"); } return 0;}
Bellman-Ford演算法:
注意之前該演算法從源點V0到頂點V的最短路經最多有n-1條邊;但在本題中,由於要找到一條迴路,回到源點V0,所以最多有n條邊,且多於n條邊是沒有必要的,因為如果存在套匯的話,n條邊構成的迴路就能形成套匯,具體看代碼注釋。
AC代碼:
#include<stdio.h>#include<string.h>#define maxn 50 //頂點數最大值#define maxm 1000 //邊數最大值#define max(a,b) ( (a)>(b) ? (a):(b) )struct exchange //匯率關係{ int ci, cj; double cij;}ex[maxm]; //匯率關係數組int i, j, k; //迴圈變數int n, m; //貨幣種類、匯率數目int flag, kase = 0;char name[maxn][20], a[20], b[20];double x, maxdist[maxn]; //匯率、源點i到其他每個頂點(包括它本身)的最長路徑長度int readcase() //讀入資料{ scanf("%d",&n); if( n == 0 ) return 0; for( i = 0; i < n; i++ ) //讀入n個貨幣名稱 scanf("%s",name[i]); scanf("%d",&m); for( int i = 0; i < m; i++ ) //讀入匯率 { scanf("%s%lf%s",a,&x,b); for( j = 0; strcmp(a,name[j]); j++ ) ; for( k = 0; strcmp(b,name[k]); k++ ) ; ex[i].ci = j; ex[i].cij = x; ex[i].cj = k; } return 1;}//Bellman-Ford演算法;以頂點v0為源點,求它到每個頂點(包括它本身)的最大距離void bellman( int v0 ){ flag = 0; memset(maxdist,0,sizeof(maxdist)); maxdist[v0] = 1; for( k = 0; k < n; k++ ) //從maxdist(0)遞推到maxdist(1),...,maxdist(n) { for( i = 0; i < m; i++ ) //判斷每條邊,加入它是否能使得最大距離增加 { if( maxdist[ex[i].ci] * ex[i].cij > maxdist[ex[i].cj] ) maxdist[ex[i].cj] = maxdist[ex[i].ci] * ex[i].cij; } } if( maxdist[v0] > 1.0 ) flag = 1;}int main(){ while( readcase() ) //讀入貨幣種類 { for( i = 0; i < n; i++ ) { bellman(i); //從第i個頂點出發求最長路徑 if( flag ) break; } if(flag) printf("Case %d: Yes\n",++kase); else printf("Case %d: No\n",++kase); } return 0;}