背包问题学习总结(九)(5.8)

01背包问题

01背包,戳这里

完全背包问题

基本题型:背包的体积为m,有n类物品它们个数不限,每类物品有不同的价值v和不同的体积w,计算背包最多能装多少价值的物品。
和01的区别:01的个数限制,每类只有一件,要么取要么不取,完全的个数不限,每类可以取1件或多件或不取。
普通方法:

memset(d,0,sizeof(d))for(i=1; i<=n; i++)
{
        for(j=0; j<=m; j++)
        {
              for(k=0;k*w[i]<=j;k++)
              {    if(j-w[i]>=0)
                      d[i][j]=max(d[i-1][j],d[i-1][j-k*w[i]]+k*v[i]);
                  else d[i][j]=d[i-1][j];
              }
        }
} 
cout<<d[n][m]<<endl;

同01背包相似,多了一层循环。
优化:01背包时,因为每类物品1件,所以考虑当前位置取与不取,是在前一类的基础上计算的,反映到代码中,就是每一类用一个i,而完全背包,每类多件,我们不仅要考虑前一类的情况,还要考虑当前类取的情况(取与不取,取多少件),实际上是在本类取的件数上一件件增加考虑的,优化如下:

memset(d,0,sizeof(d));    
for(i=1; i<=n; i++)
{
      for(j=w[i]; j<=m; j++)
      {
             d[j]=max(d[j],d[j-w[i]]+v[i]);
      }
}
cout<<d[m]<<endl;

与01的区别是内循环从小到大循环,这样就做到了本类从取少件到多件的考虑。
详细解释在01里讨论过了01背包,戳这里

多重背包问题

基本题型:有n种物品和一个容量为m的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
和完全背包的区别:每类不是无限件,而是限制了件数,一般大于一件。
普通方法:

memset(d,0,sizeof(d))for(i=1; i<=n; i++)
{
        for(j=0; j<=m; j++)
        {
              for(k=0;k<=n[i]&&k*w[i]<=j;k++)
              {    if(j-w[i]>=0)
                      d[i][j]=max(d[i-1][j],d[i-1][j-k*w[i]]+k*v[i]);
                  else d[i][j]=d[i-1][j];
              }
        }
} 
cout<<d[n][m]<<endl;

优化:考虑用一维数组,转化成01背包问题,考虑到二进制思想,一个数一定可以由2的不同幂之和表示。比如:3=1+2;5=1+4;6=2+4;7=1+2+4……,我们按照这种思想把第i种物品分成若干件不同的物品,其中每个不同的物品有一个系数,这些系数分别为1,2,4,……2^(k+1),且k是满足n[i]-2^k+1>0的最大整数,这件新物品的费用和价值均是原来的费用和价值乘以这个系数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品,(取6是为了不和前面的重复,保证新分成的每个都是不同的物品)。
二进制的不同优化方法:

  for(i=0;i<n;i++)
  {
        for(j=1;j<=a[i].num;j<<=1){ //分成不同的件,<<=等同于*=2
            v[t]=j*a[i].v;
            w[t]=j*a[i].w;
            t++;
            a[i].num-=j;
        }
        if(a[i].num>0){     //最后一项补齐
            v[t]=a[i].num*a[i].v;
            w[t]=a[i].num*a[i].w;
            t++;
        }
    }

或者

for (j = 1; j * a[i] <= m && j <= x; x -= j, j *= 2)
	b[++t] = j * a[i];
if (x) 
   b[++t] = x * a[i];//补齐最后一项

一篇很好的博客:
poj1014完全背包 hdu2191多重背包 经典二进制优化

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值