動態規劃 揹包問題

2021-10-23 15:35:05 字數 4395 閱讀 8723

題目:有乙個容量為 v 的揹包,和一些物品。這些物品分別有兩個屬性,體積 w 和價值

v,每種物品只有乙個。要求用這個揹包裝下價值盡可能多的物品,求該最大價值,揹包可以不被裝滿。

0-1揹包問題:在最優解中,每個物品只有兩種可能的情況,即在揹包中或者不在揹包中(揹包中的該物品數為0或1),因此稱為0-1揹包問題。

步驟1-找子問題:子問題必然是和物品有關的,對於每乙個物品,有兩種結果:能裝下或者不能裝下。第一,包的容量比物品體積小,裝不下,這時的最大價值和前i-1個物品的最大價值是一樣的。第二,還有足夠的容量裝下該物品,但是裝了不一定大於當前相同體積的最優價值,所以要進行比較。由上述分析,子問題中物品數和揹包容量都應當作為變數。因此子問題確定為揹包容量為j時,求前i個物品所能達到最大價值。

步驟2-確定狀態:由上述分析,「狀態」對應的「值」即為揹包容量為j時,求前i個物品所能達到最大價值,設為dp[i][j]。初始時,dp[0]j為0,沒有物品也就沒有價值。

步驟3-確定狀態轉移方程:由上述分析,第i個物品的體積為w,價值為v,則狀態轉移方程為

j[j]= dp[i-1]

[j]//揹包裝不下該物品,最大價值不變

j>=w, dp[i]

[j]= max

//和不放入該物品時同樣達到該體積的最大價值比較

#include

intmax

(int a,

int b)

//取最大值函式

struct thing

list[

101]

;int dp[

101]

[1001];

intmain()

for(

int i =

0; i <= s; i++

) dp[0]

[i]=0;

//初始化二維陣列

for(

int i =

1; i <= n; i++

)//迴圈每個物品,執行狀態轉移方程

for(

int j = list[i]

.w -

1; j >=

0; j --)}

printf

("%d\n"

, dp[n]

[s]);}

return0;

}

直接按照狀態轉移方程正序遍歷j也可以,上方**只是為了和優化演算法統一。

#include

intmax

(int a,

int b)

//取最大值函式

struct thing

list[

101]

;int dp[

101]

[1001];

intmain()

for(

int i =

0; i <= s; i++

) dp[0]

[i]=0;

//初始化二維陣列

for(

int i =

1; i <= n; i++

)//迴圈每個物品,執行狀態轉移方程

}printf

("%d\n"

, dp[n]

[s]);}

return0;

}

變式:要求恰好裝滿揹包時,把dp[0][0]設為0,其餘dp[0][i]設為負無窮即可,這樣只有恰好達到dp[n][s]時,dp[n][s]才為正值(用優化演算法也可以)。

#include

const

int inf =

-999999

;int

max(

int a,

int b)

//取最大值函式

struct thing

list[

101]

;int dp[

101]

[1001];

intmain()

dp[0]

[0]=

0;for(

int i =

1; i <= s; i++

) dp[0]

[i]= inf;

//初始化二維陣列

for(

int i =

1; i <= n; i++

)//迴圈每個物品,執行狀態轉移方程

for(

int j = list[i]

.w -

1; j >=

0; j--)}

printf

("%d\n"

, dp[n]

[s]);}

return0;

}

優化演算法:

觀察狀態轉移方程的特點,我們發現dp[i][j]的轉移只與dp[i-1][j-list[i].w]和dp[i-1][j]有關,即僅與二維陣列本行的上一行有關。因此,我們可以將二維陣列優化為一維陣列。不過這裡要注意兩點:1.j優化後的狀態轉移方程:dp[j] = max

複雜度分析:其狀態數量為ns,

n為物品數量,s為揹包總體積,狀態轉移複雜度為o(1),所以綜合時間複雜度為o(ns),優化後的空間複雜度僅為o(s)。

#include

intmax

(int a,

int b)

//取最大值函式

struct thing

list[

101]

;int dp[

1001];

intmain()

for(

int i =

0; i <= s; i++

) dp[i]=0

;//初始化二維陣列

for(

int i =

1; i <= n; i++

)//迴圈每個物品,逆序遍歷j執行狀態轉移方程

}printf

("%d\n"

, dp[s]);

}return0;

}

我們擴充套件0-1揹包問題,使每種物品的數量無限增加,便得到完全揹包問題:有乙個容積為 v 的揹包,同時有 n 個物品,每個物品均有各自的體積 w 和價值 v,每個物品的數量均為無限個,求使用該揹包最多能裝的物品價值總和。

正好利用了上述0-1揹包的優化演算法,這時我們正序遍歷j,正好可以實現每種物品的重複利用,即相當於每種物品有無限個。

#include

intmax

(int a,

int b)

//取最大值函式

struct thing

list[

101]

;int dp[

1001];

intmain()

for(

int i =

0; i <= s; i++

) dp[i]=0

;//初始化二維陣列

for(

int i =

1; i <= n; i++

)//迴圈每個物品,正序遍歷j執行狀態轉移方程

}printf

("%d\n"

, dp[s]);

}return0;

}

多重揹包問題介於 0-1 揹包和完全揹包之間:有容積為v的揹包,給定一些物品,每種物品包含體積 w、價值 v、和數量 k,求用該揹包能裝下的最大價值總量。

與之前討論的問題一樣,我們可以將多重揹包問題直接轉化到 0-1

揹包上去,即每種物品均被視為k種不同物品,對所有的物品求0-1揹包。其時間複雜度為o(s*σki)。

由此可見,降低每種物品的數量 ki 將會大大的降低其複雜度,於是我們採用一種更為有技巧性的拆分。將原數量為 k

的物品拆分為若干組,每組物品看成一件物品,其價值和重量為該組中所有物品的價值重量總和,每組物品包含的原物品個數分別為:為:1、2、4…k-2^c+1,其中

c 為使 k-2^c+1 大於 0

的最大整數。這種類似於二進位制的拆分,不僅將物品數量大大降低,同時通過對這些若干個原物品組合得到新物品的不同組合,可以得到 0 到 k

之間的任意件物品的價值重量和,所以對所有這些新物品做 0-1 揹包,即可得到多重揹包的解。由於轉化後的 0-1

揹包物品數量大大降低,其時間複雜度也得到較大優化,為o(s*σlog2(ki))。

動態規劃 揹包問題

給定n個物品,重量是,價值是,包的容量 承重 是w 問,放入哪些物品能使得包內價值最大 1 需要將問題轉化為子問題,通過遞迴實現,且子問題必然與父問題存在關聯 2 定義v i,j 表示為,當item取自前i個items且揹包capacity j 時,揹包問題的最優解,也即最高的價值。3 從前i個it...

動態規劃 揹包問題

不廢話,直接上 動態規劃,揹包問題。輸入為 int n 物品的種類數。int n weight 各件物品的重量。int n value 各種物品的價值。int w 揹包最大的裝載重量。輸出 v n b 的值,最大的裝載價值。x n 各類物品的裝載數量。author huangyongye publi...

動態規劃 揹包問題

1 開心的金明 問題描述 金明今天很開心,家裡購置的新房就要領鑰匙了,新房裡有一間他自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說 你的房間需要購買哪些物品,怎麼布置,你說了算,只要不超過n 元錢就行 今天一早金明就開始做預算,但是他想買的東西太多了,肯定會超過媽媽限定的n 元。於是,他把每...