【背包问题】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]);
          }
      }
}

猜你喜欢

转载自blog.csdn.net/qq_33618962/article/details/85925394