多重背包

处理何种问题:给定n种物品和一个容量为V的背包,物品i的体积为vi,其价值为pi,以及数量numi,求其最终可以装进背包的物品最大价值。

 

性能:时间复杂度为O(nV)

 

原理:客观上来说,可以将其直接看做01背包(只不过相同的物品很多),但会因为物品数量太多造成超时。针对这个问题,我没将物品数量进行分段处理,例如面对一个体积为6,价值为4,数量为10的物品,可以将其转化为一下这几个物品:

数量为1

体积为6

价值为4

数量为2

体积为6*2

价值为4*2

数量为4

体积为6*4

价值为4*4

数量为3

体积为6*3

价值为4*3

这些数量的物品可以组成1~10任意数量的物品。

以此再将其套用01背包,进行处理。

进行分段时的规律是1,2,4,8,16,32,64……()还有一段凑不够下一段的余数。

 

实现步骤:先对数量进行分段+01背包

 

备注:不要忘记那一段凑不够的余数,有了那段余数才能保证1~num的任意数都可以组成。

 

输入样例解释

3 6 //n、V

2 2 5//第一种物品的体积、价值、数量

3 3 8

1 4 1

输出样例解释

9 //所能构成的最大价值

 

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int MaxN=100010;
struct node
{
    int p,v;
};
node arr[MaxN];
int dp[MaxN];
int N,V;

int main()
{
    int ctor,k,v,p,num;
    while(~scanf("%d%d",&N,&V))
    {
        ctor=1;
        for(int i=1; i<=N; ++i)
        {
             scanf("%d%d%d",&v,&p,&num);//将 num 拆成:1,2,4,8,16......这样无论如何都能组成想要的物品大小,又可以避免直接转化为01背包造成的物品太多
             k=1;
             while(num>k)
             {
                 num-=k;
                 arr[ctor].v=k*v;
                 arr[ctor].p=k*p;
                 k*=2;
                 ++ctor;
             }
             arr[ctor].v=num*v;
             arr[ctor].p=num*p;
             ++ctor;

        }

        memset(dp,0,sizeof(dp));
        for(int i=1; i<ctor; ++i)
        {
            for(int j=V; j>=arr[i].v; --j)
            {
                dp[j]=max(dp[j],dp[j-arr[i].v]+arr[i].p);
            }
        }
        printf("%d\n",dp[V]);
    }
    return 0;
}

  

posted @ 2018-08-18 21:05  逃往火星的猫  阅读(116)  评论(0编辑  收藏  举报