背包九讲伪代码总结

参考链接https://www.cnblogs.com/jbelial/articles/2116074.html

N件物品, 背包容量为V。第i件物品的费用是c[i],价值是w[i]。针对题目要求, 求价值总和最大的方法。 

1.01背包(每个物品个数为1)

for(int i = 1; i <= N; i++)
    for(int v = V; v >=0; v--)     //从后向前扫描避免同一物品放入多次。
        f[v]=max{f[v],f[v-c[i]]+w[i]};   

2.完全背包(物品个数不限)

 首先说明一点,当我们在做完全背包问题时,完全可以对物品进行一次筛选,使最后所留的物品按费用从低到高排序时,他们的价值也必须是从低到高的。也就是说我们去掉了比某一物品费用高但价值反而不高于它的这个物品,物不美价不廉的物品我们要它有什么用呢?

for(int i = 1; i <= N; i++)
    for(int v = 0; v <= V; v++) 
        //恰恰相反,由于个数不限,所以同一物品能放多少放多少,才能保证当前的最大价值
        f[v]=max{f[v],f[v-c[i]]+w[i]}; 

3.多重背包(引入一个数组n,表示第i个物品最多有n[i]个)

说明:首先我们联想到可以将将第i种物品分成n[i]个物品,其中每件物品有一个系数(1~n[i]),这件物品的费用和价值均是原来的费用和价值乘以这个系数,然后转化成01背包来做,(当然也可以当完全背包来做,但是需要在代码中加以限制,即放入的数量不超过n[i])

优化:使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品(这里解释一下,1,2,4,6是可以组成1-13之间的任意数的,这样原本需要转换成13个物品,现在只需要转成4件物品即可)

 

转换的代码就是在输入时对输入的物品进行分块处理成多个物品,一个for循环即可。

核心伪代码同01背包。

4.混合三种背包(即物品的数量有以上三种情况)

for i=1..N 
    if 第i件物品是01背包 
        for v=V..0 
            f[v]=max{f[v],f[v-c[i]]+w[i]}; 
    else if 第i件物品是完全背包 
        for v=0..V 
            f[v]=max{f[v],f[v-c[i]]+w[i]}; 

//其中多重背包处理成01背包,这里我想可以维护一个n数组,1代表01背包,INF代表完全背包

5.二维背包(背包有两种容量)

背包有两个或多个限制条件是使用。二维数组维护即可。。这里对物品没有限制,所以针对题目请结合以上的问题进行解答。

​
for(int i = 1; i <= N; i++)
{
    for(int v = V; v >=0; v--) 
        for(int u = U; u >=0; u--)
            f[v][u]=max{f[v][u],f[v-c[i]][u-b[i]]+w[i]};   //01从后往前
}

​

6.分组背包(N个物品被分为K组,每组只能取1件物品)

这里加个组内的for循环即可(这里代码我改了,参考文章里写错了)

for(int i=1;i<=K;i++)
    for(int v=V;v>=0;v--)
        for( each 物品j in 第i组 )
            f[v]=max(f[v],f[v-c[j]]+w[j]);
            //对于每一个位置,取组内使当前容量价值最大的物品。

7.泛化物品(概念)

所谓泛化物品,就是给定一个物品的费用,有一个函数(或者是数组等等)能够直接对应出这个物品的价值。

   这里引用一下:一个费用为c价值为w的物品,如果它是01背包中的物品,那么把它看成泛化物品,它就是除了h(c)=w其它函数值都为0的一个函数。如果它是完全背包中的物品,那么它可以看成这样一个函数,仅当v被c整除时有h(v)=v/c*w,其它函数值均为0。如果它是多重背包中重复次数最多为n的物品,那么它对应的泛化物品的函数有h(v)=v/c*w仅当v被c整除且v/c<=n,其它情况函数值均为0。 

其实我们求解背包问题最后得出的数组f就是一个泛化,一个背包容量v对应一个当前v所能达到的最大消费f[v]。个人认为这才是泛化的核心:总结出每一个容量所对应的最优情况,将其泛化成一个物品组,来解决接下来所讲的问题。

8.有依赖的背包问题 (若选物品i(附件),则必须选物品j)

  这里大家肯定可以想到依赖情况是很复杂的(按照背包问题的一般思路,仅考虑一个主件和它的附件集合。可是,可用的策略非常多,包括:一个也不选,仅选择主件,选择主件后再选择一个附件,选择主件后再选择两个附件……无法用状态转移方程来表示如此多的策略。(事实上,设有n个附件,则策略有2^n+1个,为指数级。) )。这里就用到刚刚讲到的泛化了。

  我们可以对主件i的“附件集合”先进行一次01背包,得到费用依次为0..V-c[i]所有这些值时相应的最大价值f'[0..V-c[i]]。那么这个主件及它的附件集合相当于V-c[i]+1个物品的物品组,其中费用为c[i]+k的物品的价值为f'[k]+w[i]。也就是说原来指数级的策略中有很多策略都是冗余的,通过一次01背包后,将主件i转化为 V-c[i]+1个物品的物品组,就可以直接应用分组背包的算法解决问题了。

  当依赖关系以图论中“森林”的形式给出(森林即多叉树的集合),也就是说,主件的附件仍然可以具有自己的附件集合,限制只是每个物品最多只依赖于一个物品(只有一个主件)且不出现循环依赖。 
  解决这个问题仍然可以用将每个主件及其附件集合转化为物品组的方式。唯一不同的是,由于附件可能还有附件,就不能将每个附件都看作一个一般的01 背包中的物品了。若这个附件也有附件集合,则它必定要被先转化为物品组,然后用分组的背包问题解出主件及其附件集合所对应的附件组中各个费用的附件所对应的价值(说简单了就是先01背包后循环使用分组背包)。

 

 

2019年4月2日23:18:42,很晚了,先就说到这吧。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值