最近沉迷於演算法研究中,被poj上id為3700的這道題折磨了兩天後,終於順利通過了。深深震撼於程式效能因搜尋方法的不同而產生的天壤之別。
該題描述如下:給出一組互不相同的整數,求可將其劃分為遞變(遞增或遞減)序列的最少個數。例如:給出5個數3,5,2,4,1,最少可將其劃分為2個序列。第乙個序列為3,4,第二個序列為5,2,1。
我首先想到用多叉樹逐層計算。每個節點存放乙個陣列a, a中儲存還未劃分的原始資料,擴充套件該節點時,遍歷a,找出所有以a[0]為首的遞變序列,每找到乙個遞變序列,就為該節點增加乙個子節點,並將a中剩餘的資料存放到該子節點中。這樣每個節點可能會有多個子節點。逐層擴充套件,當某個節點無法擴充套件時,所有的資料就劃分完畢,這個節點的層數就是這種劃分方式的序列個數。
我嘗試用廣度優先擴充套件構造樹。因為僅需要得到乙個無法擴充套件的節點,就可以退出計算。提交了第乙個版本,結果mle(memory limit exceed)。稍微修改下,將節點中靜態分配的記憶體改為動態的按需分配,第二個版本tle(time limit exceeded)。我估計在資料量很大時,每個節點的子節點都很多,造成同一層上需要計算的節點數量太大而導致超時。 於是我改為深度優先擴充套件,再加些判別條件用於剪枝。第三個版本,依然tle。
難道是演算法本身的問題?我從poj的討論組中找到乙個通過的版本,用貪心法加dfs,大致思想如下:
依據此思想我寫了第四個版本,終於順利通過了。欣喜之餘,我比較了一下此方法和前面超時的第三個版本在效能上的差異。我用了20個輸入資料,比較結果如下:
版本三用了1797ms,而版本四用了0ms(小於1ms)。 速度差異至少千倍。然後我用30個資料測試,結果如下:
這個結果讓我非常震驚:版本三用了132235ms (難怪tle),而版本四仍然用了0ms。速度差異達到數十萬倍。同樣是dfs, 其實這兩個版本的主要差別是搜尋方法的不同。思考了良久,粗淺的發現了以下2個原因:
為解決大量的資料處理問題,我們學了很多演算法,往往都是為了找到乙個最優的搜尋方法。這種搜尋方法因具體問題而異。需要我們在做題中積累經驗並且善於歸納、聯想、對比來確定最優方案。這個最優方案往往會使程式效能有超乎想象的提高。
poj 3687 反向拓撲尋求字典序最小解
看題意可得,這道題很明顯是反向拓撲排序求解的問題,只不過反向拓撲排序求的是標籤的順序,之後還有把每個標籤所對應的質量聯絡起來就好。include include include include include includeusing namespace std const int maxn 210...
poj2135Farm Tour 最小費用最大流
題目要求從1到n走一遍再從n到1走一遍而且有重邊,相當於從1到n走兩邊 所以加乙個源點0 1,費用為0,流量為2 加乙個匯點n 1 n,費用為0,流量為2 中間的邊流量為1 只走一遍 費用為c 然後一遍0 n 1的費用流 要注意是無向圖,所以乙個輸入有4條邊 a b c,1 b a c,0 b a ...
RMQ 求區間最值(poj 3264)
1.概述 rmq range minimum maximum query 即區間最值查詢,是指這樣乙個問題 對於長度為n的數列a,回答若干詢問rmq a,i,j i,j n 返回數列a中下標在i,j之間的最小 大值。這兩個問題是在實際應用中經常遇到的問題,下面介紹一下解決這兩種問題的比較高效的演算法...