Bootstrap

多重背包问题(二进制优化)

题目描述:

有N种物品和一个容量是V的背包,第i种物品最多有si件,每件体积是vi,价值是wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输入格式:

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容量。

接下来有N行,每行三个整数vi,wi,si,用空格隔开,分别表示第i种物品的体积、价值和数量

输出格式:

输出一个整数,表示最大价值。

数据范围:0<N≤10000<N≤1000
0<V≤20000<V≤2000
0<vi,wi,si≤20000<vi,wi,si≤2000

提示:

本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

思路分析: 

本道题目如果采用把多重背包问题转换成01背包问题,因为每个数字可以用二进制的形式来表示,所以把每个物品数量的二进制位上的1给打包,每次往左移一位,因此个数为1、2、4、8、16、32.......如果碰到不是这些数,就把剩下的另外打包即可,例如3就是把1和2先打包,再把剩下的1单独打包。以上提到的打包都是指当前数量的价值和容积。

算法分析: 

一个正整数n,可以被分解成1,2,4,…,2^(k-1),n-2^k+1的形式。其中,k是满足n-2^k+1>0的最大整数。

例如,假设给定价值为2,数量为10的物品,依据二进制优化思想可将10分解为1+2+4+3,则原来价值为2,数量为10的物品可等效转化为价值分别为1*2,2*2,4*2,3*2,即价值分别为2,4,8,6,数量均为1的物品。

代码实现: 

#include<stdio.h>
int max(int a,int b)
{  if(a>b)return a;
    return b;
}
int dp[15000];
int vj[15000];
int wj[15000];
int main()
{   int n,v,vi,wi,si,i,j,k,count=1;
    scanf("%d %d",&n,&v);//物品个数和背包容量

    for(i=1;i<=n;i++)
    {   scanf("%d %d %d",&vi,&wi,&si);//物品的体积、价值和数量

        for(k=1;k<=si;k<<=1)
         {  vj[count]=k*vi;
            wj[count++]=k*wi;
            si-=k;
         }
         if(si>0)
         {   vj[count]=si*vi;
             wj[count++]=si*wi;
         }

    }
     for(i=1;i<=count;i++)
      for(j=v;j>=vj[i];j--)
        dp[j]=max(dp[j],dp[j-vj[i]]+wj[i]);

        printf("%d",dp[v]);
        return 0;


}


;