动态规划之背包问题

动态规划之背包问题

参考《背包问题九讲》

问题描述:

给定一个容量为N的背包,给定m件物品,每件物品价值为w[i]、费用为c[i],求如何选择物体可以使物体的费用不超过背包的总容量,且背包能够带走的最大的价值。
1. 如果限定每件物品只有一件,则为0-1背包问题;
2. 如果每件物品的数量不受限制,则为完全背包问题;
3. 如果限定每件物品的数量为n[i]件,则为多重背包问题;
4. 将1、2和3的情况融合,即,有的物品只有一件、有的物品数量不受限制、有的数量为n[i]件,则为混合背包问题;
5. 当选择一件物品必须同时付出两种代价v[i]和u[i]时,是多重费用背包问题;
6. 当物体之间有冲突,某几个物体中只能选择一个时,为分组背包问题;
7. 当选择A物体的前提是必须选择B物体,则为有依赖的背包问题

思路

背包问题属于典型的动态规划问题,需要使用状态转移方程求解,必须理解状态转移方程的含义。在下面求解过程中,将各个问题进行抽象,以伪代码的形式给出解答。

1. 0-1背包问题:

f[i][v]表示:对于容量为v的背包,在给定物体中只考虑前i件物品,所能获得的最大总价值。
状态转移方程:
f[i][v] = max{f[i-1][v], f[i-1][v-c[i]]+w[i]}
这种解法需要维护一个m*N的二维数组,然后从上到下、从左到右地建立数组,最终数组右下角的值既为能带走的最大value。
伪代码:

for irow = 1 <-- bagsize
    for jobj = 1 <-- object_number
        if cost[jobj] > irow:
        # bag cannot hold the object
        else:
        # bag can hold the object

代码实现:GitHub

我们可以将空间复杂度可以从O(mxN)优化为O(N),即使用一维数组求解0-1背包问题,在每次地推f[i][v]时,让v由大到小变化即可。f[v]含义为:(设处理数组第i个元素)尺寸为i的背包能够带走的最大价值
伪代码:

zero_one_package(cost, worth, bagsize,f):
    for v = bagsize <-- 0
        f[v] = max{f[v-cost]+worth, f[v]}
for i = 1 <-- object_num
    zero_one_package(c[i], w[i], bagsize, f)

代码实现:GitHub

0-1背包优化思路:
idea1:对于循环for v = bagsize <-- 0,可以优化下界:for v = bagsize <-- cost[i],即当cost[i]超过bagsize时,此物体必然不能装入bag

2. 完全背包

思路1:由于每件物体的数量不受限制,我们可以采用二进制的思想将一件物品变换为等价的k件物品,它们的cost和value分别为n*c和n*value,n = 2^{k}, k=0,1,..,[log2(n/w)]。不论最终对此件物品选择多少件,都可以等价于在拆分过的物体集合上求解0-1背包问题。
思路2:基于0-1背包的求解思路,递推f[v]时,让bagsize由小到大的变化.
伪代码:

complete_package(cost, worth, bagsize,f):
    for v = 0 <-- bagsize
        f[v] = max{f[v-cost]+worth, f[v]}
for i = 1 <-- object_num
    zero_one_package(c[i], w[i], bagsize, f)

代码实现:GitHub

3. 多重背包

记第i种物品有n[i]件,则对于第i种物品共有n[i]+1个选择策略。多重背包问题与完全背包问题类似,状态转换方程可以写为:
f[i][v] = max{f[i-1][v-k*c[i]] + k*w[i]}, 0 <= i <=n[i]
即,遍历特定物体所能出现的各种情况,选择效果最优的
我们可以对物体进行拆分,从而降低时间复杂度。记第i种物品有n[i]件,则拆分的系数为: 1,2,4,...,2k1,n[i]2k+1 1 , 2 , 4 , . . . , 2 k − 1 , n [ i ] − 2 k + 1 ,将物体的费用和价值分别乘以这些系数,得到拆分后等价的物体,物体就被拆分为 log(n[i])+1 l o g ( n [ i ] ) + 1 件物品。
代码:GitHub

4. 混合背包

在一个背包问题中,一些物体只有一件(01背包)、一些物体有无穷多件(完全背包)、一些物体有有限数目件(多重背包)。对于容量给定的背包,如何选择物体,使得可以带走最多的价值?
求解混合背包问题,先写出01背包、完全背包和多重背包这三种问题对应的程序。在迭代中,对于具体的物体按照具体的程序进行迭代即可。

5. 二维费用的背包问题

当费用增加一维时,状态也增加一维,状态转化方程为:
f[i][u][v]=max{f[i1][u][v],f[i1][uc[i]][vd[i]]+w[i]} f [ i ] [ u ] [ v ] = m a x { f [ i − 1 ] [ u ] [ v ] , f [ i − 1 ] [ u − c [ i ] ] [ v − d [ i ] ] + w [ i ] }

6. 分组背包问题

每一个分组的选择策略包括:选择本组中的某一件物品或者一件物品也不选,按组进行迭代
for i所有组k:
for f = bag size <– 0
for 组k中的所有物体
f[v] = max{f[v], f[v-c[i]]+w[i]}

7. 有依赖的背包问题

首先,考虑在一个主件和它的附件集合中进行选择,若含有n个附件,则选择的策略有 2n+1 2 n + 1 种,范围巨大。由于各个策略之间互斥,考虑对于费用相同的策略,只保留价值最高的一个。采用“01背包”方法处理附件集合,得到长度为v-c[i]+1的物品组及其对应的最大价值。在此基础上再采用分组背包问题的方法求解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值