題目:有乙個容量為 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 元。於是,他把每...