多重背包问题

来自《挑战程序设计竞赛》

1.题目原文

有n中物品,它们的重量和价值分别是w[i]和v[i]。现在要从中选出一些物品使得总重量不超过W,并且价值的总和最大。不过要求第i中物品最多可以选m[i]个。
数据范围
1<=n<=100,1<=w[i],v[i]<=100,1<=m[i]<=10000,1<=W<=10000

2.解题思路1

这是一个有个数限制的问题,对于每个物品至多选一个或者可以选任意个的问题我们已经在O(nW)时间内求解。如果使用同样的方法解答本题,则状态转移方程为:
dp[i][j]:从编号为0到编号为i-1,前i个物品总重量不超过j的所有选法中最大可能的价值。
有dp[0][j]=0;dp[i+1][j]=max{dp[i][j-k*w[i]]+k*v[i]|0<=k<=m[i]且j-k*w[i]>=0}。
如果使用这个状态转移方程,时间复杂度就是O(nmW)。

3.代码1

int n,W;
int w[maxn],v[maxn],m[maxn];
int dp[maxn][maxW];

void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=W;i++){
        dp[0][i]=0;
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<=W;j++){
            for(int k=0;k<=m[i]&&j>=k*w[i];k++){
                dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
            }
        }
    }
    printf("%d\n",dp[n][W]);
}
这个算法时间复杂度较高,无法在规定时间内求解。

4.解题思路2

5.代码2

6.解题思路3

我们把m[i]分解成如下形式:m[i]=1+2+2^2+……2^k+a(0<=a<2^(k+1))
由于1,2,2^2,……2^k可以表示出0~2^(k+1)-1的所有整数,因此,1,2,2^2,……2^k,a可以表示出0~m[i]的所有整数。因此,我们可以把m[i]个重量和价值分别是w[i],v[i]的物品看成重量和价值分别是w[i]*x,v[i]*x(x=1,2,2^2,……,2^k,a)的k+2个物品,这样物品的总数就变为O(nlogm)个,使用一般的01背包DP可以在O(nWlogm)时间内求出答案。

7.代码3

int n,W;
int w[maxn],v[maxn],m[maxn];
int dp[maxW];

void solve()
{
    for(int i=0;i<n;i++){
        int num=m[i];
        for(int k=1;num>0;k<<=1){
            int mul=min(k,num);
            for(int j=W;j>=w[i]*mul;j--){
                dp[j]=max(dp[j],dp[j-w[i]*mul]+v[i]*mul);
            }
            num-=mul;
        }
    }
    printf("%d\n",dp[W]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值