DP--多重背包--HDU2191

多重背包二进制拆分实现

跟完全背包一样的道理,利用二进制的思想将n[i]件物品i拆分成若干件物品,目的是在0-n[i]中的任何数字都能用这若干件物品代换,另外,超过n[i]件的策略是不允许的。

方法是将物品i分成若干件,其中每一件物品都有一个系数,这件物品的费用和价值都是原来的费用和价值乘以这个系数,使得这些系数分别为1,2,4,…,2^(k-1),n[i]-2^k+1,且k满足n[i]-2^k+1>0的最大整数。例如,n[i]=13,就将该物品拆成系数为1、2、4、6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。

状态转移方程: dp( i, v ) = max { dp( i-1, v-k*c[i] ) + k * w[i] | 0 <= k <= num[i] }


下面给出 O( log M ) 时间处理一件多重背包中物品的过程:

def MultiplePack(F, C, W, M)
    if C*M >= V
        CompletePack(F, C, W)
        return
    k := 1
    while k < M
        ZeroOnePack(kC, kW)
        M := M - k
        k := 2k
    ZeroOnePack(C*M, W*M)



#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 110;
int c[maxn], w[maxn], num[maxn], dp[maxn], n, m;

void complete_pack(int cost, int weight)
{
	for(int i=cost; i<=n; i++)
		dp[i] = max(dp[i], dp[i-cost] + weight);
}

void zero1_pack(int cost, int weight)
{
	for(int i=n; i>=cost; i--)
		dp[i] = max(dp[i], dp[i-cost] + weight);
}

int multi_pack()
{
	memset(dp, 0, sizeof(dp));
	for(int i=0; i<m; i++) {
		if(num[i]*c[i] > n) complete_pack(c[i], w[i]);
		else {
			int k = 1;
			while(k < num[i]) {
				zero1_pack(k*c[i], k*w[i]);
				num[i] -= k;
				k <<= 1;
			}
			zero1_pack(num[i]*c[i], num[i]*w[i]);
		}
	}
	return dp[n];
}

int main()
{
	int T;
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &n, &m);
        for(int i=0; i<m; i++) scanf("%d%d%d", &c[i], &w[i], &num[i]);
        int ans = multi_pack();
        printf("%d\n", ans);
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值