開學以來的乙個半月做了不少動規(水)題,那就寫寫題解吧。
1. vijos 1059 積木城堡:
一開始看到這是浙江省選題頓時嚇尿。但其實是大水題(主要體現在資料弱)。
用揹包求出每個城堡能達到的高度,取所有城堡都能達到的最大高度即可。
具體一點:
用 f(i, j, k) 表示對於第 i 個城堡,用前 j 個積木能否拼成 k 的高度,則
f(i, j, k) = f(i, j-1, k) or f(i, j-1, k-h(j))
邊界 f(i, 0, 0) = true
然後滾動陣列滾起來。如果 f(i, j, k) 為 true,就給 hash[k]++,最後掃一遍 hash 陣列找到最大的 k 滿足 hash[k]=n,輸出。
2. vijos 1014 旅行商問題簡化版
先按照點的橫座標排序。
問題可以轉化為兩個人同時從第乙個點出發走兩條不相交(除起點終點外沒有共同點)的路徑到達最右邊的點所走的最短路徑。
用 f(i, j) 表示第乙個人走到了點 i 、第二個人走到了點 j 時的最短路徑長度(因為 f(i, j)=f(j, i),所以我們假設 i>j),則
若 j=i-1,f(i, j) = dist(k, i)+min,1<=k若 j3. vijos 1037搭建雙塔
其實這道題還是不錯的,但是資料很弱所以就被我無腦水過去了。
用 f(i, j, k) 表示能否用前 i 個水晶搭建高分別為 j 和 k 的兩座塔,則
f(i, j, k) = f(i-1, j, k) or f(i-1, j-h(i), k) or f(i-1, j, k-h(i))
邊界條件 f(0, 0, 0) = true
理論上這樣做的最多操作次數可能達到 4 億。但是由於資料弱,還是被本蒟蒻水過去了。
更好的做法是用 f(i, j) 表示用前 i 個水晶搭建高度相差為 j 的兩座塔中較低的塔最高是多少,則
f(i, j) = max
看起來很不好理解,其實決策就是把第 i 個水晶放在高的塔還是矮的塔。如果放在高塔上,那麼高塔與矮塔之前的差為 j-h(i);如果放在矮塔,這裡有兩種情況:1)矮塔變成了較高的塔,那麼之前的差應該是 h(i)-j;2)矮塔依然是矮塔,那麼之前的差是 h(i)+j。畫個圖會更加清楚。
4. vijos 1006晴天小豬歷險記之hill
這是一道看起來不那麼水的題目。
這裡我們先把問題看做是從上往下走,然後很快可以寫出乙個有點問題的動規方程。用 f(i, j) 表示走到第 i 行第 j 列時的最短路徑,則
f(i, j) = min
對應四種決策:往左走、往右走、往左下走、往右下走。而邊界的時候,比如當 j=i 時意味著在最右邊一列,再往右走會走到第 1 列。很明顯,這裡有個死迴圈在裡面。
而我的解決方法是邊界處特殊處理。從右邊界往右走到達左邊界,繼續往右走會回到右邊界。顯然,在第 i 行的右邊界往右走不會超過 i 步,否則相當於走了一圈,沒有任何意義反而增加了路徑長度。
特殊處理並不難,只要列舉從右邊界開始往右走最後停在哪個點然後往下走即可。
左邊界也是一樣的道理。(題目裡沒有說清楚的是從右邊界往右可以到左邊界,從左邊界往左也可以到右邊界)
但是這種方法比較另類。比較普遍的做法是某些人所說的「暴力 dp」,其實就是對於乙個階段內的狀態並不止計算一次,如果有乙個狀態的值更新了就會導致另一些狀態的值更新,直到所有狀態的值都不再更新為止,這樣就有效避免了死迴圈。這裡有點 spfa 的味道,所以 lzw 大神就直接用 spfa 做了。
5. noip2013day2t3 花匠
後來聽說了這道題的 o(n) 演算法之後真是一口老血吐在螢幕上。
明確一下題目的意思是去掉最少的元素使得乙個序列成為波浪形上下起伏。為了敘述方便我們稱這樣的序列為合法序列。
我們可以用 f(i, 0) 表示以 i 這個元素結尾且 a(i) 小於前乙個元素的合法序列的最大長度,用 f(i, 1) 表示以 i 這個元素結尾且a(i) 大於前乙個元素的合法序列的最大長度,則
f(i, 0) = max
如果直接暴力找 j,那麼複雜度是 o(n^2) ,無法承受。
然後 lzw 大神用的是線段樹來維護最大值。因為常數比較大所以稍慢。
而我發現這個方程很像 lis,lis 維護了乙個有序序列+二分查詢使得複雜度變為 o(nlogn),那麼這道題應該也是可以的。於是我們記x(i) = min, f(j, 1)=i,第乙個方程就可以變成
f(i, 0) = k+1,k 是滿足條件 x(k)>a(i) 的第乙個元素
那麼這個 k 就可以通過二分查詢來得到(x 陣列的遞增性是顯然的)。
同理,可以另設乙個 y 陣列充當類似的作用。
但是很關鍵的一點是,x 陣列的更新和 lis 是不一樣的。得到了乙個 f(i, 1) 之後可能需要更新許多 x 值,這裡的迴圈次數不好估計。所以我這樣也算是碰運氣過了?不過事實證明迴圈次數不會太多。至少從時間上來看遠小於線段樹的方法。
至於 o(n) 神演算法就是統計「拐點」的個數,因為最優解不過就是包含了所有這些拐點而已。orz。
做過的一些水題的總結
在藍橋杯決賽的前一天,我整理了下這個部落格,從一月份左右到現在現在一共是五十四篇,艾瑪我寫居然這麼多了,加上去年暑假開始,也差不多一年了。至今的水平也是非常一般,所以做的都是入門級別的水題。回首一年之前,從台州oj到藍橋杯題庫到zoj到poj,也是一段有趣的歷程很多題目的難度應該屬於 熟練語言 級別...
刷usaco水題的一些啟示
就是斷斷續續刷了一些銀組的題,雖說真的有點水,因為這些題大多是簡單轉化一下模型就可以了,但還是有一些啟示吧 bzoj1618 完全揹包的方程要理解好 1 include2 include3 include4 using namespace std 5const int maxh 50005 6 co...
區間動規經典題 石子合併
在乙個園形操場的四周擺放n堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分 試設計出1個演算法,計算出將n堆石子合併成1堆的最小得分和最大得分 資料的第1行試正整數n,1 n 100,表示有n堆石子.第2行有n個數,分別表示每堆石...