1. 程式人生 > >【揹包問題】01揹包 多重揹包 完全揹包

【揹包問題】01揹包 多重揹包 完全揹包

01揹包

0-1揹包問題是指每一種物品都只有一件,可以選擇放或者不放。

  • 方法一
    V(i,j)表示前i種物品恰放入一個容量為j的揹包的最大價值,因此狀態轉移方程:
  1. j<w(i) V(i,j)=V(i-1,j)
  2. 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]);
          }
      }
}