多重背包
Posted clearmoonlight
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多重背包相关的知识,希望对你有一定的参考价值。
我在之前讲过0-1背包和完全背包,这里讲多重背包。不同于0-1背包和完全背包,多重背包中每个物品有个给定的数量。假定背包容量为m,有n个物品,每个物品的重量为weight[i], 价值为value[i], 数目为num[i]. 显然,多重背包可以转化为0-1背包问题:将num[i]个物品i看作是num[i]个不同的物品,那么一共有Σnum[i]个物品,算法的复杂度为O(mΣnum[i]).
这里介绍一个更有效的算法。上面转化成0-1背包其实是把num[i]分成num[i]个1之和。考虑把num[i]分解成一个集合S,满足{1,2,...,num[i]}中的任一个数都可以由S的一个子集之和来表达。借用二进制,把num[i]表示成如下形式
num[i]=2^0+2^1+2^2+...+2^k+num[i]-(2^(k+1)-1),k=floor(log(num[i]+1))-1.
这里,S={2^0,2^1,2^2,...,2^k,num[i]-(2^(k+1)-1)}. 举个例子,num[i]=13, 那么k=2, S={1,2,4,6}, 可以验证
1=1
2=2
3=1+2
4=4
5=1+4
6=2+4
7=1+2+4
8=2+6
9=1+2+6
10=4+6
11=1+4+6
12=2+4+6
13=1+2+4+6
经过对num[i]如此操作之后,问题重新变成了0-1背包问题,但是算法复杂度降为了O(mΣlog(num[i])). 核心代码如下:
void ZeroOnePack(int wi, int vi) { int i; for (i = m; i >= wi; i--) dp[i] = max(dp[i], dp[i - wi] + vi); } void CompletePack(int wi, int vi) { int i; for (i = wi; i <= m; i++) dp[i] = max(dp[i], dp[i - wi] + vi); } void MultiplePack(int wi, int vi, int ni) { if (m <= ni*wi) CompletePack(wi, vi); else { int k = 1; while (k <= ni) { ZeroOnePack(k*wi, k*vi); ni -= k; k <<= 1; } ZeroOnePack(ni*wi, ni*vi); } } memset(dp, 0, sizeof(dp)); for (i = 0; i<n; i++) MultiplePack(weight[i], value[i], num[i]);
参考以下博文:
https://blog.csdn.net/qq_38984851/article/details/81133840
以上是关于多重背包的主要内容,如果未能解决你的问题,请参考以下文章