多重背包

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

以上是关于多重背包的主要内容,如果未能解决你的问题,请参考以下文章

动态规划问题3--多重背包

蒟蒻吃药计划-治疗系列 #round4 多重背包+混合背包代码存放

解题报告:hdu2191汶川地震 - 多重背包模板

POJ 3260 多重背包+完全背包

01背包模板全然背包 and 多重背包(模板)

01背包+完全背包+多重背包+单调队列