二进制拆分,无话可说
题目描述
终于,破解了千年的难题。小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;
}