DP的二进制拆分优化

经典例题:多重背包
洛谷 P1776 宝物筛选 https://www.luogu.com.cn/problem/P1776
输入:
第一行是整数 n 和 W,分别表示物品种数和背包的最大容量。
接下来 n 行,每行三个整数 vi​、wi​、mi​,分别表示第i个物品的价值、体积、数量。
输出:
输出一个整数,表示背包不超载的情况下装入物品的最大价值。

题解:例如第i种物品有mi​=25个,这25个物品放进背包的组合,有0~25的26种情况。不过要组合成26种情况,其实并不需要25个物品。根据二进制的计算原理,任何一个十进制整数X,都可以用1、2、4、8…这些2的倍数相加得到,故可以将25个物品分为1,2,4,8,10倍于原物品的新物品。
代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXX=100010;
int n,W,dp[MAXX];
int v[MAXX],w[MAXX],m[MAXX]; 
int new_n;                               //二进制拆分后的新物品总数量
int new_v[MAXX],new_w[MAXX],new_m[MAXX]; //二进制拆分后新物品

int main(){
    cin >> n >>W;  
    for(int i=1;i<=n;i++)  cin>>v[i]>>w[i]>>m[i];
//以下是二进制拆分
	int new_n = 0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m[i];j<<=1) {   //二进制枚举:1,2,4...
			m[i]-=j;                   //减去已拆分的
			new_w[++new_n] = j*w[i];   //新物品
			new_v[new_n]   = j*v[i];       
		}
		if(m[i]){                      //最后一个是余数
			new_w[++new_n] = m[i]*w[i];
			new_v[new_n]   = m[i]*v[i]; 
		}
	}
//以下是滚动数组版本的0/1背包
	for(int i=1;i<=new_n;i++)              //枚举物品
		for(int j=W;j>=new_w[i];j--)       //枚举背包容量
			dp[j]=max(dp[j],dp[j-new_w[i]]+new_v[i]);

    cout << dp[W] << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值