多重背包的两种解决方法(poj1276)

多重背包是指装入背包的若干见物品种,可以拿的数量有上限。


第一种就是二进制优化,转换成01背包,例如一件物品可以拿26件,每一件的权值是w,26可以写成(1+2+4+8)+11,所以就把这种物品分解成权重为w,2w,4w,8w,11w的五种物品,这五种物品组合,一定能组成小于等于26的任意一个数,这样就把有26件的一种物品换成了五种各有一件的物品,用01背包就能解决了。

poj1276的代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<climits>
#include<list>
#include<stack>
#define mem(a) memset(a,0,sizeof(a))

using  namespace std;

int dp[100002];
int w[200];
int main()
{
    int ans_v;
    while(scanf("%d",&ans_v)!=EOF)
    {
        int t=0;
        int n;
        scanf("%d",&n);
        int i;
        for(i=0;i<n;++i)
        {
            int mi,wi;
            scanf("%d%d",&mi,&wi);
            int base=1;
            int sum=1;
            while(sum<=mi)
            {
                w[t]=wi*base;
                base*=2;
                sum+=base;
                t++;
            }
            if(sum-base<mi)
            {
                w[t++]=(mi-(sum-base))*wi;
            }
        }
        mem(dp);
        for(i=0;i<t;++i)
        {
            for(int j=ans_v;j>=w[i];--j)
            {
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
            }
        }
        printf("%d\n",dp[ans_v]);

    }
}

而另一种方法是维护一个数组num,num[i][j]表示在往背包里试着装第i件物品时,背包容量使用了j时装了多少件i物品。cost[i]为物品i的体积,num[i][j]=num[j-cost[i]]+1;可以看到num[i][j]和num[t|t!=i][j|j=1,2,....v]没有任何关系,所以可以省掉第一维,对于每一种物品,清空num,只计算num[j]就行了。

同样是poj1276的代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<climits>
#include<list>
#include<stack>
#define mem(a) memset(a,0,sizeof(a))

using  namespace std;

int wi[20];
int mi[20];
int dp[100050];
int num[100050];

int main()
{
    int ans_v;
    while(scanf("%d",&ans_v)!=EOF)
    {
        int n;
        mem(dp);
        mem(num);
        scanf("%d",&n);
        int i;
        for(i=0;i<n;++i)
        {
            scanf("%d%d",&mi[i],&wi[i]);
        }
        for(i=0;i<n;++i)
        {
            int j;
            mem(num);
            for(j=wi[i];j<=ans_v;++j)
            {
                if(dp[j-wi[i]]+wi[i]>dp[j]&&num[j-wi[i]]<mi[i])
                {
                    dp[j]=dp[j-wi[i]]+wi[i];
                    num[j]=num[j-wi[i]]+1;
                }
            }
        }
        printf("%d\n",dp[ans_v]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值