一、01揹包
(1)空間優化——一維數組以前不明白爲什麼這裏需要用倒序來推導,現在看明白了。
因爲在每一次主循環開始時,dp數組存貯着的就是i-1的狀態。
而如果是順序的,那麼假如當前更新到了dp[j],此時的dp[j-c[i]]是已經在本輪中更新過了的,相當於是dp[i][j-c[i]]而不是dp[i-1][j-c[i]]。
這張截圖也說得很清楚了。
(2)時間小優化——
第二層循環只進行到cost
(3)初始化的細節——
由兩種不同的提問方式來區分
如果是“要求恰好裝滿”,那麼則要使dp[0]=0,而dp[1...W]=-inf.
如果是“不要求恰好裝滿”,那麼dp{0...W]=0.
這是我自己的解釋:
因爲我們只用一個數組,那麼dp初始化的值在將來什麼時候用都不確定。
如果是“恰好裝滿”,那麼每個dp的含義都應該是是代表已經裝了j重量的物品後可以獲得的最大價值。
那麼當前任一個dp[j](j!=0)值都不可能是零,因爲它裏面必須裝有東西!!!
除非存在價值爲零的商品,且所有其他商品的價值都比零小,而這種題目一般不會出現吧。
如果是“非恰好裝滿”,那麼每個dp的含義應該是代表容量大小爲j的揹包可以獲得的最大價值。
而這個時候,dp[j]爲零就是合法的。
(4)一個常數優化
前面的僞代碼中有for v=V..1,可以將這個循環的下限進行改進。
由於只需要最後f[V]的值,倒推前一個物品,其實只要知道f[V-w[n]]即可。以此類推,對以
第j個揹包,其實只需要知道到f[V-(後n-j個物品的重量總和)]即可,即代碼中的
二、完全揹包:
(1)最最基礎的思路:
(2)但是上面這個時間複雜度太高,可以加一個優化,
(3)接下來是一種更爲簡單有效的辦法。
我們慣常是以+1來枚舉的。但是對於某當前的剩餘容量V,和當前的物品,
我們最多可以拿V/c[i] 個,而這個數我們可以用二進制來表示,也就是說我們
只需要確定重量爲1c[i],2c[i] ,3c[i] ,4c[i] ...的商品拿一件還是不拿就可以了
這樣我們就回歸到了01揹包的問題之中。
那麼複雜度就是O{N·V·logV/c[i]}
(4)但是上面的始終是三層循環,實際上我們有O{N·V}的算法。
我自己的理解是,對於每一個狀態dp[j](不管是當前i還是上一個循環的i-1)我們都可以選擇“維持當前狀態不拿商品”,也可以“拿商品”。在下一個狀態dp[j+c[i]]也是同樣操作。這樣一來就是實現了從一種物品一個不拿到一個物品拿多個的各種情況的最優化選擇。
三、多重揹包:
記住!只有形如 dp[i]=max/min (f[k]) + g[i] (k<i && g[i]是與k無關的變量)才能用到單調隊列進行優化。
優化的對象就是f[k]。