题目描述
终于,破解了千年的难题。小 (FF) 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
这下小 (FF) 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 (FF) 的采集车似乎装不下那么多宝物。看来小 (FF) 只能含泪舍弃其中的一部分宝物了。
小 (FF) 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 (FF) 有一个最大载重为 (W) 的采集车,洞穴里总共有 (n) 种宝物,每种宝物的价值为 (v_i),重量为 (w_i),每种宝物有 (m_i)件。小 (FF) 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。
输入格式
第一行为一个整数 (n) 和 (W),分别表示宝物种数和采集车的最大载重。
接下来 (n) 行每行三个整数 (v_i,w_i,m_i)。
输出格式
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
输入输出样例
输入 #1
4 20
3 9 3
5 9 1
9 4 2
8 1 3
输出 #1
47
说明/提示
对于 (30%) 的数据,(nleq sum) (m_ileq 10^4),(0le Wleq 10^3)。
对于 (100%) 的数据,(nleq sum) (m_i leq 10^5),(0le Wleq 4 imes 10^4),(1leq nle 100)。
思路
学过小学奥数的应该都知道,(2^n)以内的数(2)的幂可以通过加和通过得到(2^{n+1})以内的所有数,那么就可以利用这个特殊的性质来加速多重背包和完全背包。原本的多重背包需要三层循环一个个物品枚举,这样是立方复杂度,非常慢。而优化之后就是平方再乘以对数,相当于是平方再乘了个比较大的常数,可以大大降低时间复杂度。那么具体怎么优化呢?因为可以选多个一样的物品,所以我们就可以合并其中的多个,来表示所有可以取到的可能性,转化为01背包来做,具体看代码加注释。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int n,m,cnt;
int v,v1[100005],w,w1[100005],f[100005],s;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&v,&w,&s);//因为我们将每种物品转化为了01背包来做,所以不用开数组存储,可以节省空间
int p=0;
while(1){
int t=pow(2,p);//二进制优化
if(t<s){//如果还能接着拆,那么相当于将几个物品合并为了一个,同时也不影响可以枚举到所有的情况
s-=t;
v1[++cnt]=v*t;
w1[cnt]=w*t;
p++;
}
else{//如果不能拆了,比如现在还剩3个物品,但是t=8,那么就直接将这个加进去即可,以表示所有的可能,同时别忘记结束循环
v1[++cnt]=v*s;
w1[cnt]=w*s;
break;
}
}
}
for(int i=1;i<=cnt;i++){
for(int j=m;j>=w1[i];j--){
f[j]=max(f[j],f[j-w1[i]]+v1[i]);//01背包
}
}
printf("%d
",f[m]);
return 0;
}