01揹包之再理解

2021-07-02 01:08:49 字數 3840 閱讀 5813

想要學習動態規劃,從01揹包問題入手,看了許多大牛的部落格,各種優化各種講解,講的都不錯,但是都是快速閱讀,不求甚解,幾天下來除了記住狀態轉移方程之外無甚收穫,而且學的囫圇吞棗,說會不算會,說不會又冤得慌,如鯁在喉,終於下定決心搞個清楚,梳理整齊。

注意,這些東西都不是我自己的想法,只是想把看的理解的東西記下來。會引用甚至摘抄原文。

1.1 、01揹包的題目: 有n件物品和乙個容量為v 的揹包。放入第i件物品耗費的空間是wi,得到的價值是vi。求解將哪些物品裝入揹包可使價值總和最大。

1.2、 基本思路  :這是最基礎的揹包問題,特點是:每種物品僅有一件,可以選擇放或不放。

用子問題定義狀態:即dp[i, v]表示前i件物品恰放入乙個容量為v的揹包可以獲得的最大價值。則其狀態轉移方程便是:

dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+v[i]);(大寫v表示可用空間,小寫v指當前物品的價值(value) )

每一件物品選擇放或者不放,就是動態規劃中的決策,而dp【i】【v】及其意義就是狀態,這兩點很重要,非常重要。(因為我們面對一道動態規劃題目。首先要識別子問題(把問題的規模縮小,先寫出邊界情況,用歸納法嘗試找規律,然後取任意值i,考慮i的狀態與i-1的狀態之間的轉換關係) ,然後定義狀態,最後根據決策來建立狀態轉移方程。)

這個方程非常重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:「將前i件物品放入容量為v的揹包中」這個子問題,若只考慮第i件物品的策略(放或不放),那麼就可以轉化為乙個只和前i − 1件物品相關的問題。如果不放第i件物品,那麼問題就轉化為「前i − 1件物品放入容量為v的揹包中」,價值為dp[i − 1, v];如果放第i件物品,那麼問題就轉化為「前i − 1件物品放入剩下的容量為v − wi的揹包中」,此時能獲得的最大價值就是dp[i − 1, v − wi]再加上通過放入第i件物品獲得的價值vi。

偽**: 

dp[0, 0..v ] = 0

for i = 1 to n

for j = wi to v

dp[i, v] = max

c:10   n:5

wi      vi

4        9

3        6

5        1

2        4

5        1

0

1

2

3

4

5

6

7

8

9

10

0

1

2

0   

0   

0   

6   

9   

9   

9   

15 15 

15 15 

3

4

5

#include#includeusing namespace std;

int main()

}cout先貼**: 

將i方向的維數去掉, 我們可以將原來二維陣列表示為一維資料:d(i-1, j-v)變為d(j-v), d(i-1, j)變為d(j)。當我們要計算d(i, j)時,只需要比較d(j)和d(j-v)+w的大小, 用較大的數更新d(j)即可。等等,如果我要計算d(i, j+1),而它恰好要用到d(i-1, j)的值, 那麼問題就出來了,因為你剛剛才把它更新為d(i, j)了。那麼,怎麼辦呢? 按照j遞減的順序即可避免這種問題。比如,你計算完d(i, j), 接下來要計算的是d(i,j-1),而它的狀態轉移方程為d(i, j-1)=max,它不會再用到d(i-1,j)的值!所以, 即使該位置的值被更新了也無所謂。

仔細想想這段話哈,再看看圖就明白了,當我們求dp[i][j]的時候用到了dp[i][j-v]和dp[i][j],那麼又怎麼會阻礙dp[i][j+1]了呢?想想我們在求dp【i】【j】的時候就可能把dp【i-1】【j】或者dp【i】【j-v】改變了,(由於是一維陣列,被改變之後就被替換了),但是當j增加的時候,dp【i】【j+1】又可能用到原來的dp【i-1】【j】,但是遺憾的是他已經不是原來的他了(真正的原因是為了保證每件物品只是用一次,對比完全揹包的內迴圈就是順序的,也就是說完全揹包的物品可以多次使用)。。。這樣就不好了麼,那麼怎麼辦呢,讓內迴圈從大到小唄,等於是二維陣列的每一行都從右往左填充,這樣的話填充過dp【i】【j+1】再填充dp【i】【j】這樣使用上一行的陣列就不會起衝突了。

然後呢?然後敵人向我們提出了更加苛刻的要求。。。。。他們想要知道都有哪些物品被裝進了揹包,而哪些沒有裝。。。。

這個方法待我慢慢抄來。。。上**

int j = v;

for(int i=n; i>0; --i)

}for(int i=1; i<=n; ++i) printf("%d ", x[i]);

解釋: 

讓我們先定義乙個陣列x,對於其中的元素為1時表示對應編號的物品放入揹包, 為0則不放入。舉個例子,對於體積為5,4,3,價值為20,10,12的3個寶石 ,如何求得其對應的陣列x呢?(明顯我們目測一下就知道x=, 但程式可目測不出來)ok,讓我們還是從狀態說起。如果我們把3號放入了揹包, 那麼是不是也就意味著,前3個物品放入揹包的最大價值要比前2個物品放入揹包的價值大, 即:dp(3, 10)>dp(2, 10)。再用字母代替具體的數字 (不知不覺中我們就用了不完全歸納法哈),當dp(i, j)>dp(i-1, j)時,x(i)=1;

完整**: 

#include#includeusing namespace std;

int main()

} cout<= 1;i--)

} for(int i = 1;i <= n;i++)

cout<

完 了?還沒有。。。。

揹包九講裡面還有乙個問題提到, 初始化的細節問題,我們看到的求最優解的揹包問題題目中,事實上有兩種不太相同的問法。

有的題目要求「恰好裝滿揹包」時的最優解,有的題目則並沒有要求必須把揹包裝滿。一種區別這兩種問法的實現方法是在初始化的時候有所不同。如果是第一種問法,要求恰好裝滿揹包,那麼在初始化時除了f[0]為0,其它f[1..v ]均設為−∞,這樣就可以保證最終得到的f[v ]是一種恰好裝滿揹包的最優解。如果並沒有要求必須把揹包裝滿,而是只希**盡量大,初始化時應該將f[0..v ]全部設為0。這是為什麼呢?可以這樣理解:初始化的f陣列事實上就是在沒有任何物品可以放入揹包時的合法狀態。如果要求揹包恰好裝滿,那麼此時只有容量為0的揹包可以在什麼也不裝且價值為0的情況下被「恰好裝滿」,其它容量的揹包均沒有合法的解,屬於未定義的狀態,應該被賦值為-∞了。如果揹包並非必須被裝滿,那麼任何容量的揹包都有乙個合法解「什麼都不裝」,這個解的價值為0,所以初始時狀態的值也就全部為0了。這個小技巧完全可以推廣到其它型別的揹包問題。

講的多好呀。。。

揹包問題之01揹包

01揹包就是說針對每一件物品,有選擇裝入或者放棄,是屬於動態規劃類的問題。現在假設我們有m件物品,各有價值,揹包承重為10,假設當前可用的承重量為v,當前在抉擇第m件物品是否放入,重量為w1,價值為v1,那麼,如果不放入,我們的價值應該保持不變,並與之前算出的最大價值相同,如果放入,放入後的價值就是...

揹包dp之01揹包

現在我們有n個配件,他們有不同的價值.但是我們揹包的容量是有限的,因為我們只有乙個一級包,所以我們最多可以裝v重量的東西.但是為了能更好的吃到雞 不存在的 我們要攜帶更有價值的配件,請問我們最多能拿多少價值的配件來當快遞員呢?輸入的第一行是t,表示有一共要打t場比賽.每組資料由三行組成.第一行包含兩...

揹包問題之0 1揹包

0 1揹包是最基本的揹包問題,其核心思路就在於每個物品的放與不放 每個物品最多只能放一次 題目有 n 個物品和乙個大小為 m 的揹包.給定陣列 a 表示每個物品的大小和陣列 v 表示每個物品的價值.問最多能裝入揹包的總價值是多大?樣例輸入 m 10,a 2,3,5,7 v 1,5,2,4 輸出 9 ...