背包九讲之——其中四讲

背包九讲之——其中四讲

  背包,顾名思义,就是一个背包。(……),其实就是在一个背包中装东西,运用各种高端操作,而装出各种奇怪的东西的一种算法。

 

 看题目就知道,背包一共被分为九种,各种背包所对应的题目和解法也是非常奇怪的啊。

 

那么种类如下:

   ①0/1背包

   ②完全背包

   ③多重背包

   ④混合背包

   ⑤二维费用的背包

   ⑥分组的背包

   ⑦依赖背包

   ……等等(请原谅!(Please excuse me!))

 

那么我们今天就是说一说前四种,也就是0/1背包、完全背包、多重背包以及混合背包这四种。

 

那么我们接下来分块复习。

 

 

一、0/1背包

  可以说接下来的一切背包问题中0/1背包是基础。

  下面的绝大多数奇怪的以及奇怪的以及奇怪的背包都是有0/1背包转换而来。

 

  原谅我(Excuse me!)!背包真的没有什么概念可言,我们直接上例题。

    问题描述:有n件物品和一个容量为C的背包。第i件物品的重量为w[i],价值为v[i].问将那些物品放入背包可使获得的价值最大。

 

    分析:eng~~贪心?每次找最大值?呵呵~你想多了,一个数据直接卡死。

   例如:C为10.接下来的价值及其对应重量为:

   价值:10  8  7 

   重量:10   5  5

  例如这组数据,正解是取价值为8和7的物品。而贪心却炸了

 

  那么我们接下来想正解:

   思想:动态规划

  那么我们就要相想出状态转移方程

  我们开始想:一件物品,只有两种状态,一种是取,一种是不取,那么我们就可以想出以下状态转移方程:

   f[i][j]=max(f[i][j],f[i][j-w[i]]+v[i])

    f[i][j]表示前i件物品恰放入背包中的最大价值

 

那么我们继续想:如果将数据很大是呢??不是会MLE吗??我们就继续优化。

   我们通过观察二维数组的状态转移方程发现f[i][c]只与f[i-1]这一层有关系,所以,我们考虑将i这层维数给优化掉。进一步发现f[i][c]只与f[i-1][c]和f[i-1]和f[i-1][c-w[i]]有关,如果想把i这个维数省掉的话,第i层的f[c]只与第i-1层的f[c]和f[c-w[i]]有关,因为f[c-w[i]]在f[c]的左边,所以我们在求f[c]的时候,必须保证f[c-w[i]]还是第i-1阶段的最优值,在下面代码的递推时,c要从C开始,倒着推,只有这样,才能保证f[c]左边的状态没有被第i个物品更新过,还是i-1层的状态。

  代码如下:

     for (inti=1;i<=n;i++)

       for (int c=C;c>=0;c--)

         if (c>=w[i])f[c]=max(f[c],f[c-w[i]]+v[i]);

 

那么这就是0/1背包

二、完全背包

   问题描述:有n件物品和一个容量为C的背包。第i件物品的重量为w[i],价值为v[i].每件物品可以放无数次。问将那些物品放入背包可使获得的价值最大。

 

分析:既然我们之前说道0/1背包倒的循环是为了避免更新重复的状态。那我们是否再从反面取想:我们是否可以将刚刚的0/1背包的倒得循环转换成正的循环呢??

  事实上就是这样

    那么代码如下:

     for (inti=1;i<=n;i++)

       for (int c=0;c<=C;c++)

         if (c>=w[i])f[c]=max(f[c],f[c-w[i]]+v[i]);

绝望吧~吧~吧~

 

三、多重背包

  问题描述:将n间物品和一个容量为C的背包。第i种物品的重量是w[i],价值为v[i],数(shù)(liàng)a[i] 求最大的价值。

 

 分析:二进制法:按照二进制法分割物品。比方说,物品i有13个,就可以把它分成系数为1、2、4、6共四个0/1背包的物品。13=(2^0+2^1+2^2+6),或者说1、2、4、6可以组成1——13中的任何数字,所以,这4个数字可以代替1——13

 

那么代码如下:

  for (int i=1;i<=n;i++)

    {

       if (w[i]*a[i]>C){//如果物品够多,其实就是完全背包问题

        for (int c=0;c<=C;c++)//完全背包

           if (c>=w[i]) f[c]=max(f[c],f[c-w[i]]+v[i]);

      }

       else {

         int k=1,ant=a[i];

         while(k<ant){

            //是否去一个重量为k*w[i],价值为k*v[i]的物品?

           for (int c=C;c>=k*w[i];c--)

             f[c]=max(f[c],f[c-k*w[i]]+k*v[i]);

             //继续分割

             ant-=k;k+=k;

        }

         //吧剩下的作为单独一个物品

          for (int c=C;c>=ant;c--)

   f[c]=max(f[c],f[c-ant*w[i]]+ant*v[i]);

 

 

  四、混合背包

  问题描述:还是背包问题,只是有的物品只能取一次(0/1背包),有的物品能去无限次(完全背包),有的物品只能取有限次(多重背包)。如何解决?

 

分析:额~~这道题不就是前三题的综合吗~~只不过再加个判断罢了,其他就是模板啊!那么具体伪代码如下:

      for (int i=1;i<N;i++){

        if (物品属于0/1背包)

          //0/1背包解法

     }

   else if (物品属于完全背包)

        //完全背包解法

     else if (物品属于多重背包)

        //多重背包解法

 

 

怎么样??这种操作高端吧??

好,那么今天的背包还是就复习到这里,这四种背包还是非常重要的!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值