P1776 宝物筛选_NOI导刊2010提高(02) 背包+二进制拆分

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_44343213/article/details/102669418

二进制拆分,无话可说

题目描述
终于,破解了千年的难题。小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物……这下小FF可发财了,嘎嘎。但是这里的宝物实在是太多了,小FF的采集车似乎装不下那么多宝物。看来小FF只能含泪舍弃其中的一部分宝物了……小FF对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小FF有一个最大载重为W的采集车,洞穴里总共有n种宝物,每种宝物的价值为v[i],重量为w[i],每种宝物有m[i]件。小FF希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式
第一行为一个整数N和w,分别表示宝物种数和采集车的最大载重。

接下来n行每行三个整数,其中第i行第一个数表示第i类品价值,第二个整数表示一件该类物品的重量,第三个整数为该类物品数量。

输出格式
输出仅一个整数ans,表示在采集车不超载的情况下收集的宝物的最大价值。

输入输出样例
输入 #1
4 20
3 9 3
5 9 1
9 4 2
8 1 3
输出 #1
47
说明/提示
对于30%的数据:n≤∑m[i]≤104;0≤W≤103。

对于100%的数据:n≤∑m[i]≤10^5;

0 <w≤4*10^4:1≤n<100。

解法:二进制拆分

刚学二进制拆分,一看到这道题秒切???

还有二进制拆分比单调队列好打多了啊

看到这道题,我们可以用多重背包来解决,但是数据太大了,多重背包那n^3的时间复杂度绝对会炸

于是我们考虑用二进制拆分

核心思想:因为每个数都可以用二进制数来表示,所以我们可以把他拆为2的次方和,对于每一部分我们用01背包来解决

因为这道题的w≤4 * 10^4, 而 2^16=65536,所以对于每个物品只要开16个空间就可以了,100*16=1600

AC代码

#include<cstdio>
#include<algorithm>
#define re register int
using namespace std;
int n,W,a[105],b[105],c[105];
int tp,v[2222],w[2222],f[40005];//防止RE,所以我开到了两千
inline void pre() {//二进制拆分
	for(re i=1;i<=n;i++) {
		int t=1;
		while(c[i]) {
			v[++tp]=t*a[i];
			w[tp]=t*b[i];
			c[i]-=t; t*=2;
			if(c[i]<t) {//不足再继续拆分,直接放到一个格子里
				v[++tp]=a[i]*c[i];
				w[tp]=b[i]*c[i];
				break;
			}
		}
	}
}
int main() {
	scanf("%d%d",&n,&W);
	for(re i=1;i<=n;i++) {
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
	}
	pre();
	for(re i=1;i<=tp;i++)//01背包板子
		for(re j=W;j>=w[i];j--)
			f[j]=max(f[j],f[j-w[i]]+v[i]);
	printf("%d",f[W]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44343213/article/details/102669418