下面是我以前在我的Live Space的Blog上面寫的一些ACM演算法題目的解法,一次性貼出來供大家參考,也是對自己研究的一個記錄。以後我做了新的題目會在CSDN Blog和我的Live Space Blog上面同時發布。
最近決定做一些ACM的題目來提高一下自己的演算法的水平。N年(n>=6)沒做這類型的題目了,手有點生疏了。網上有不少ACM的題目和Online Judge,UVa算是其中比較出名的一個。就從UVa開始做起吧。當時在學校就應該好好搞ACM的,可惜畢業之後才想起有ACM這回事 UVa Problem Set Submit Program的幾個注意事項:
- 輸入輸出直接從stdin/stdout,不用檔案讀寫
- 可以接受一個輸入立刻輸出答案,不用全部記住答案然後一次性輸出
- 最好用gcc/g++,避免Compile Error
- 注意IO結束條件是EOF還是其他的特殊值
UVA - Volume I - #100 這一道題看上去不難,直接一個一個硬算也可以得出答案。不過要快速得出答案自然需要用到Dynamic Programming,說白了就是要把中間結果記下來。用數組記當然最簡單但是記憶體要求還是蠻大的,所以我先用map記了。算cycle number需要遞迴,我則是用的一個stack(vector)來去掉遞迴,先反覆壓棧直到答案已知,然後依次退棧計算。 程式寫好了,提交上去居然是Compile Error? 看了一下網上的論壇,沒找到什麼有用的建議。試了一下#include老的.h的標頭檔依然Compile Error. 實在沒有辦法了,試著把int main(void)改成了int main(int argc, char *argv),居然通過了。可是我的gcc並沒有報錯啊? Compile Error好了,接下來居然是Wrong Answer。奇怪,我試過了不少答案應該是正確的阿。試著考慮一下i>j的case,還是Wrong Answer。上Forum看看,發現好多人接連試過n次都是Wrong Answer,幸好有人指點說i>j的時候i j不應該交換順序,而是原樣輸出。比如100 1輸出還是要輸出100 1,不能圖方便直接交換變成1 100。暈,題目明明沒有說清楚這些額外條件阿,難道要我們自己猜?改好了之後又修了一個Bug,終於Accepted。不過時間就。。。 似乎比他們直接計算然後比較最大值來的還慢,可能是我為了節省記憶體用了map導致要不斷進行尋找導致速度偏慢。如果用數組應該會改善不少。BTW,我看到這個題結果排名前面的不少人居然CPU已耗用時間是0!?? 就算再快也不可能快的這麼離譜吧,會不會是他們預先把結果算好瞭然後直接輸出了呢? 以後我會比較有規律的每個星期堅持做一些題目,然後把自己的解法Post出來。
UVA - Volume I - #101 開始101。這個題主要是類比機器人移動方塊,並不需要什麼技巧。用一個list<int>作為stack來維護一堆積木的狀態,用這樣的一個vector來track每個位置的積木堆的狀態。最後用一個pos的vector來track每個方塊所在的位置。 剛開始的時候Wrong Answer,後來發現看錯了題目要求,修改了一下便Accepted。這個題的通過率為25.7%,可能是題目要求比較容易出錯導致的吧。UVa - #103 - Stacking Boxes一個子問題是如何判斷一個多維的盒子可以放在另外一個多維的盒子裡面。其實只需要對各個維做一下排序然後依次順序比較即可。這個題目據我所知有兩種方法:方法1)如果盒子a可以放置到盒子b中,則認為盒子a對應的節點a有一條邊到盒子b對應的節點b。這樣,問題轉化為求All Pairs Longest Path。用動態規劃可以解。
假定dist[i, j]是i到j的最大距離,則有dist[i, j] = max { dist[i, r] + 1 | g[r, j] <> max } 計算Online Judge的Test Case用時0.088s方法2)比方法1快上不少。題目可以看成求嚴格遞增的數字子序列(Longest Scattered Subsequence)。假定s[i] = 結束於i的子序列Ax1, Ax2, ... Axm, xm=i,於是有s[i] = max { s[j] + 1 | A[j] < A[i] }很典型的Bottom-Up Dynamic Programming. 計算Online Judge的Test Case用時0.010s,大概快9倍。
UVa - #104 - Arbitrage
前幾天就已經做完了,但是一直沒有時間來寫。這道題說白了無非就是求具有最大權值之積的最短環路。所以最根本的還是直接應用Floyd-Warshall演算法,因為本題需要求最短的環路,所以在問題空間用兩維的f[i][j]是不夠的,需要用到三維: f[i][j][step]。step指path的長度。在Floyd-Warshall演算法的三層迴圈之外還需要一層step的迴圈,公式如下:
f[i][j][step] = max { f[i][k][step-1] * f[k][j][1] | f[k][j][1] < max }
這次的程式和上次的都有Presentation Error。嘗試了一下之後發現我在最後一個結果後面多Output了一個空格,去掉就Accept了。
UVa - #112 - Tree Summing
其實是一個比較簡單的題,有點奇怪為什麼正確率那麼低,可能大家忘了考慮負數了吧。Anyway,直接用遞迴下降法就可以解決。遞迴下降分析樹結構的同時就相當於遍曆了整棵樹,因此無需在記憶體中把這棵樹建立起來,而是在遞迴的時候同時累加,就可以知道是否存在這樣的路徑了。
UVa - #10405 - Longest Common Sub-Sequence
特意去找了這道題來做一下,鞏固一下自己對此類演算法的理解,順便也實驗了一下Bottom-up Dynamic Programming和Top-down Dynamic Programming的速度區別。一般來說,如果Bottom-Up Dynamic Programming沒有計算很多沒有用到的子問題的話,Bottom-Up Dynamic Programming要快於Top-Down的。這個題目比較搞的是,題目中完全沒有提到字串中居然會有空格......
作者: ATField
E-Mail: atfield_zhang@hotmail.com
Blog: http://blog.csdn.net/atfield