竹園論壇

標題: 104 - Arbitrage [打印本頁]

作者: jd3    時間: 2014-5-31 01:51
標題: 104 - Arbitrage
本帖最後由 jd3 於 2014-11-25 14:34 編輯

題意:給定匯率、求能轉換最少次數、換回原本貨幣的、獲利>=1.01% 的方法


解析(一個禮拜的心路歷程?)
首先,他是一張有向圖,沒有回到自己的邊,也沒有重邊
要找回到自己的路徑(即環),路徑長度 從邊的權重相加 改成 相乘
然後是判斷路徑大於1.01以及回復路徑


如果寫過一些有"回到原點"的最短路徑題目
例如: (痾....不見了)
對這題的直覺應該就是 Floyd-Warshall (下簡稱FW)的開心 for迴圈*3
(點的數量也才那麼一點點) (別動 ! 小心 ! )


把他換成全點對的最小路徑來處理,最小路徑換成>1.01的路線裡面更新最少次的


怎麼做呢? 首先我們想到要儲存更新次數和儲存路徑,所以開個陣列來存
for for for 爆下去然後再 for for找了最短的路徑然後再遞迴找答案
或許你覺得(其實是我以為...)一開始自己到自己設成無限大然後回來看就知道有拿裡有>1.01的環了
然後就開心的WA
(不~可能連範測都沒過OAO)


因為直接FW找出來的答案有點問題 (三層for很爽但是別急別急~)
首先最容易證明的是一個很容易WA掉的一筆測資(很討厭的)
他的答案是 1 2 1 2 1
不~他繞了兩圈!!!
咱們如果只用2維表格下去存,最後遞迴一定找不出來這條路徑


其他的錯誤就例如說誤解FW的表格的意義啦~(並不是說不可以有不同解釋)
更新條件的錯誤啦~(其實我這邊已經錯到很離譜所以接著進入AC解法)



先來瞭解一下FW
[C++] 純文本查看 復制代碼
    for(int k = 0 ; k < n ; k++)    //中繼點 
        for(int i = 0 ; i < n ; i++)
            for(int j = 0 ; j < n ; j++)
                if(dist[j] > dist[k]+dist[k][j])
                    dist[j] = dist[k]+dist[k][j];

當準備更新第k次的時候
dist[j]存的是只使用了0~(k-1)之中的點當中繼點(經過)的最短距離(不一定有使用(經過))(不經過k~(n-1))
加入第k個點當作可用的中繼點後如果答案比較好就更新
(關於原本的FW三維表格請自行google)



基於剛才發現到的問題,FW不能用了OAO
因為更新時在原本的矩陣上迭代,資訊被覆蓋了
所以現在把「更新第P次」拉出第3個維度來開表格
每次更新時在新的一層上填入,並記錄前一個點


更新時不使用 if(dist[j] > dist[k]+dist[k][j])
而是改成 if(dist[j][p] > dist[k][p-1]+dist[k][j][0])
( 假設原本的圖存在dist[j][0] )
請注意紅字的 0
+dist[k][j][0] 確保更新P次只經過P個點



見下方code的SP()
P的for迴圈包在更新外面,表示更新第P次
更新完立即檢察是否有符合條件的環可以快一些


雖然此題用這方法不考慮浮點誤差也會AC
但也可以用log()取值後路徑長就變成用+的,條件變為>= log(1.01)


[C++] 純文本查看 復制代碼

/*
    UVA 104
    AC
    56ms
*/


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>




using namespace std;


int n;
double list[32][32][32];    //    [j][p] min dist of after
int through[32][32][32];
double target = 1.01;



void recover(int i, int j, int p)
{
    if(p==0)
        return;
    if(through[j][p] != -1)
        recover(i, through[j][p], p-1);
    printf("%d ",through[j][p]);
}

void SP()
{
    for(int p = 1 ; p <= n ; p++)
    {
        //update
        for(int k = 1 ; k <= n ; k++)
            for(int i = 1 ; i <= n ; i++)
                for(int j = 1 ; j <= n ; j++)
                    if(list[k][p-1]*list[k][j][0] > list[j][p])
                    {
                        list[j][p] = list[k][p-1]*list[k][j][0];
                        through[j][p] = k;
                    }
                        
                        
        //check
        for(int i = 1 ; i <= n ; i++)
        {
            if(list[p] >= target)
            {
                printf("%d ",i);
                recover(i,i,p);
                printf("%d\n",i);
                return;
            }
        }
    }
   
    puts("no arbitrage sequence exists");
    return;
}


int main()
{
    while(~scanf("%d",&n))
    {
        //init
        memset(list,0,sizeof(list));
        memset(through, -1, sizeof(through));
        
        //input
        for(int i = 1 ; i <= n ; i++)
            for(int j = 1 ; j <= n ; j++)
                if(i!=j)
                    scanf("%lf", &list[j][0]);
               
        
        SP();
    }
    return 0;
}









歡迎光臨 竹園論壇 (http://forum.tfcis.org/) Powered by Discuz! X3.2