【揹包問題】01揹包 多重揹包 完全揹包
阿新 • • 發佈:2019-01-06
01揹包
0-1揹包問題是指每一種物品都只有一件,可以選擇放或者不放。
- 方法一
V(i,j)表示前i種物品恰放入一個容量為j的揹包的最大價值,因此狀態轉移方程:
- j<w(i) V(i,j)=V(i-1,j)
- j>=w(i) V(i,j)=max{ V(i-1,j),V(i-1,j-w(i))+v(i) }
for(int i = 0; i <= n; i++)//初始化第0列
V[i][0] = 0;
for(int j = 0; j <= C; j++)//初始化第0行
V[0][j] = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= C; j++)
if(j < a[i-1].wight)
V[i][j] = V[i-1][j];
else
V[i][j] = MAX(V[i-1][j],V[i-1][j-a[i-1].wight] + a[i-1].value);
for(int i = n,j = C; i > 0; i--){
if(V[i][j] > V[i-1][j]){//將是否放入揹包的n位向量賦值
x[i-1] = 1;
j = j - a[i-1].wight;
}
else
x[i-1] = 0;
}
- 方法二
可以使用一維陣列儲存,從上一種方法可以看出,在計算v[i][j]時只使用了v[i-1][0……j],所以使用一維滾動陣列依次覆蓋即可
for (int i=1;i<=n;i++)
for (int j=C;j>=0;j--){
if(t[i]<=j)
v[ j]=max(v[j],v[j-a[i]]+w[i]);
else v[j]=v[j];
}
內層迴圈是逆序的原因:
我們在求v[j]的時候需要用到v[j-1],如果採用正序,當到v[j]時v[j-1]已經是這一行的狀態了,沒辦法與前一行再進行比較。
完全揹包
完全揹包問題是指每種物品都有無限件可以放入揹包。完全揹包問題與01揹包問題的區別在於完全揹包每一件物品的數量都有無限個,而01揹包每件物品數量只有1個。
- 方法一
在01揹包的基礎上加入變數k代表某物品放入多少個,狀態轉移方程:
v[i][j] = max{v[i][j],v[i-1][j - k * a[i]] + k * w[I]} (0<=k*a[i]<=v)
缺點:這個方法需要三層迴圈 - 方法二
轉化為01揹包問題。
第i種物品最多能夠放入V/a[i]件,因此把第i種物品轉換為重量相同的V/a[i]件物品,再解01揹包問題即可。狀態轉移方程:
v[i][j]=max(v[i-1][j],v[i][j-a[i]]+w[i])
max函式第二個變數變化的原因:不是在上一種物品即i-1種物品的基礎上放入了,而是第i件物品多放入一件
for (int i=1; i<=n; i++)
for (int j=1; j<=C; j++)
if (a[i] <= j)
v[i][j] = max(v[i-1][j],v[i][j-a[i]]+w[i]);
else
v[i][j] = v[i-1][j];
- 方法三
使用一維陣列。不同於01揹包,多放入一件第i類物品,就要在未放入此物品的基礎上,因此使用順序迴圈。狀態轉移方程:
v[j] = max(v[j],v[j-a[i]]+w[i])
for (int i=1;i<=n;i++)
for (int j=w[i];j<=v;j++)
v[j] = max(v[j],v[j-a[i]]+w[i]);
多重揹包
多重揹包問題中,每種物品的數量是有限的。
- 方法一
把第i種物品換成n[i]件01揹包中的物品,則變成了01揹包問題 - 方法二
第i種物品有n(i)+1种放入方式,即取0件、1件……n(i)件,狀態轉移方程:
v[i][j] = max ( v[i-1][j-k * a[i] ] + k*w[i] ) 0<=k<=num[i]
for (int i=1;i<=n;i++){
f[i][0]=0;
for (int j=a[i];j<=v;j++){
int ncount=min(num[i],j/a[i]);
for (int k=0;k<=ncount;k++){
v[i][j]=max(v[i][j],v[i-1][j-k*a[i]]+k*w[i]);
}
}
}