码迷,mamicode.com
首页 > 其他好文 > 详细

多重背包

时间:2019-03-05 09:43:24      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:enter   roo   int   www.   article   进制   .net   ++   问题:   

我在之前讲过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

多重背包

标签:enter   roo   int   www.   article   进制   .net   ++   问题:   

原文地址:https://www.cnblogs.com/ClearMoonlight/p/10474476.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!