1. 程式人生 > 實用技巧 >8.4集訓

8.4集訓

上午

考試


下午

講dp!!!

我們來通過通過幾道例題來體會一下什麼是dp

01揹包

將n件有著各自體積和價值的物品裝入一個容積為m的揹包中,求最大的總價值

狀態:\(f[i][j]\)表示前\(i\)件物品放入揹包為\(j\)的揹包中所能獲得的最大總價值

轉移:\(f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])\)

結果:\(f[n][m]\)

最長不下降子序列

給出一個長度為n 的序列,求最長的子序列,滿足這些元素依次不減

狀態:設\(f[i]\)表示以\(i\)為結尾的最長不下降子序列的最長長度

轉移:\(f[i] = max(f[j]+1)\)

其中\(a[j]<a[i]\)

結果:\(max(f[i])\)

上面是\(O(n^2)\)做法,還有一種\(O(nlogn)\)的做法:

狀態:設\(f[i]\)表示長度為\(i\)的的最長不下降子序列的最後一個元素的最小值

舉個例子:

有序列\(a[] = {8,2,3,5,7,6,4}\),所以可得:

\(f[0] = 0,f[1] = 2, f[2] = 3, f[3]=5,f[4]=7\)(第一遍掃)

遇到\(6\)考慮更新\(f[4]\),遇到\(4\)考慮更新\(f[3]\)

至於為什麼是logn的,因為每次是二分查詢(\(lower\)_\(bound\)是log的)

再次考慮\(dp\)

的牛逼之處: 需要有易於表示的狀態,能夠列出的轉移,便於計算的結果

要把式子推出來再去寫程式碼

下面有幾道一般dp,沒什麼特點,就是

渡河問題

click

狀態:\(f[i]\)表示將前\(i\)頭牛運到對岸的時間

轉移:\(f[i] = min(f[j]+2*M+sum[i-j])\)其中\(sum\)為字首和

結果:\(f[n]\)

解題

click

給出貪心的\(Hack\)資料:

50 5  
40 10  
10 40  
10 5  
10 3  
10 2

狀態:\(f[i][j]\)表示處理\(1\)\(j\)的題,最後一次選擇了\(i\)\(j\)的最短月數

轉移:\(f[i][j] = min(f[k][i-1]+1/2)\)

其中剩下的錢足夠就是\(+1\)否則是\(+2\)

結果:\(min(f[i][p])+1\)