背包问题,硬币问题

至少有4种背包问题:1)01背包 2)  部分背包 3)完全背包 4)多重背包。只有部分背包是个贪心问题,其他的都是以01背包为基础的动归问题。

部分背包问题:

把物品按价值密度从大到小排序(W[i]/V[i]),然后从第一种物品开始,尽可能多拿当前物品,如果当前物品的量小于背包剩余容量,拿完,指针推到下一种物品;否则拿背包剩余容量的量,结束。

01背包问题

递推式:f(i,j )= max ( f(i-1, j), f(i-1, j-V[i])+W[i]),i定义为前i个物品,j为当前背包剩余容量。当前状态只跟上一行相同列和一个前面的列有关,滚动数组逆序求解。滚动数组初始值代表拿0个物品,不管背包容量是多少价值都是0,所以初始化全是0,行循环下界从1开始。列的计算也是从1开始,因为第0列代表容量为零,一个物品都放不下,价值必然是0,进一步的优化是从V[i]开始,因为背包容量小于V[i]的时候,当前物品i必然没办法拿,只能是f(i-1, j) 即保持上一行的状态。复杂度分析:二维动归,状态是二维的,遍历所有状态是O(N*C),计算每个状态的开销是O(1),总的复杂度是O(N*C)。


完全背包问题

每个物品不是取还是不取,而是可以取任意次数。

思路1:虽说可以取任意次,实际上每个物品可取的次数有个上限,即C/V[i]。一个物品可拿C/V[i]次可以分割为C/V[i]个只能拿一次的相同物品,转换为0,1背包。O(N' *C)

思路2:直接扩展0,1背包的思路,从2种选择(0,1)扩展到 C/V[i]种选择(0,1,2,...C/V[i])。递推式:f(i,j) = max{ f(i-1, j - k* V[i]) + k * W[i] } k=0,1,.., C/V[i]。滚动数组依然是逆序求解。复杂度分析:遍历二维状态 是O(N*C),计算每个状态是O(C/V[i]),总得复杂度是O(NC*C/V[i])

思路3:递推式:f(i, j) = max ( f(i-1, j), f(i, j - V[i]) + W[i]) 这个递推式和01背包的递推式很像,只是max里的第二项是本行的状态。递推式的含义不是按当前物品拿几次划分,而是另一种划分和解释:用前i-1个物品装容量j的最大价值,和用前i个物品,先装最大 j- V[i]的容量,然后再装一个物品i的价值,二者的大者。划分的角度是用不用到前i个物品。注意这里f的定义和01背包就不同:f(i,j)的定义是,用前i个物品,每个物品不限次数,装到容量为j的背包的最大价值。滚动数组求解是正序的。正序和逆序的区别就是,对于正序,j以前的状态是当前行的状态;对于逆序,j以前的状态是上一个行的状态。复杂度分析:遍历二维状态O(N*C),计算状态O(1),总的复杂度是O(N*C)。

思路4:直接递归的思路。考察在装任意一个物品之后面临的问题,仍然可以从n中物品中任意选择,这个条件没有变化,只是背包容量变小了,也就是说装了任意一个物品之后的面临的问题是和原问题相同的问题,只是规模变小了,这就是递归。而且仅仅是背包容量这一个维度变小了,是一维递归。反观01背包,在装一个物品之后面临的问题,不仅仅是原问题背包容量变小之后的子问题,因为问题其他条件也变了,有一个物品不能选了,必须把这个变量也作为递归变量,是二维递归。直接递归法下的完全背包问题递推式:f(j) = max { f(j- V[i]) + W[i] }, i= 1,..., n and V[i]<=C 。解释是,先取一个,然后解决剩下的问题,经典的递归、分治思想,先让规模减小一步,然后解决规模变小了的相同问题。

多重背包问题

每个物品取的次数有限制,输入多一个数组num[n]。

思路1:次数转化成物品数,变成01背包问题。

思路2:扩展01背包问题,第i个物品分别拿0到num[i]次,还要满足j-num[i] * V[i]>0


背包问题变种:硬币问题

其实01背包问题的模型是相对复杂的,输入是一两个数组,一个cost数组,一个价值数组,还有一个总cost上限,求价值最大化的值。

变种1:最少硬币问题。给定一个硬币面额集合和一个金额,求最怎样用最少的硬币凑成这个金额?输入比背包问题少一个数组,问题不如背包问题复杂。用背包问题的元素翻译一下:给定一组物体的体积,求装满一个给定容量背包所用物品的最少个数,每个物品可以任意取。

递推式:f( j) = min { 1+ f(j-V[i-1]) } i= 1,.., n and V[i-1]<= j,先分别取一个每一个可以取的硬币(面额小于给定的金额),然后递归解决金额变小了的子问题,得到分别先取每一个硬币的取法的最小硬币数,然后取最小的。

思路2:完全背包,f(i, j) = min( f(i-1, j), f(i, j - v[i-1])) 

变种2:方案数问题,给定一个硬币面额集合和一个金额,求用这面额的硬币凑成这个金额的方案数。

递归思路:完全背包:不取第i种面额的方案数+ 取第i种面额的方案数。递推式:f(i, j) = f(i-1, j) + f(i, j - v[i - 1])

变种3:给定n种硬币,从中取k个,求方案数。

思路:和上一题是一样的,只不过背包体积换成背包可以装的个数,每一个物品的体积cost变为数目cost为1,完全背包问题: f[i][j] = f[i][j-1] + f[i-1][j]




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值