题意: 裸多重背包,但是题目数据范围较大。三重循环纯朴素解法会超时。
思路:
- 为什么会超时? 对于每种物品,如果每次在遍历每种物品和每个体积的时候都枚举一遍物品数量,那么时间复杂度会到O(n^3),n=1000时,计算量就到了10亿次的级别,而c++一秒只能算1亿次,所以必须考虑优化。
- 怎么优化? 对于每个物品如果暴力枚举所有数量会超时,那么考虑将一部分物品分组。我们可以将物品按二进制的方式进行分组:1,2,4,8,,,2^k,C。前k+1项很好理解,分别是2的0~k次方,C是什么?因为我们要通过二进制分组的方式,表达出某个物品的任意数量,那么分组之后的总和也应该等于该物品的数量,C就是该物品的总数量依次减去前k+1项后的值。
- 为什么可以这样优化? 首先我们必须了解一点,二进制分组的方式可以表示出所有可能,比如:1,2,4,8。是可以表示出0~15任意的一个数的。 为什么要强调这一点?因为二进制分组同样遍历了所有的可能。 那么二进制和普通的枚举区别在哪? 二进制的遍历是在不同体积下体现的,而普通的枚举则是每种体积都会枚举一次。
概括而言:二进制将体积的变化与物品不同数量的枚举结合在了一起。
代码:
#include#include using namespace std; const int N=25000,M=2000; int n,m; int v[N],w[N]; int f[M]; int main() { cin>>n>>m; int cnt=0;//表示将数据处理为01背包问题后,总物品的个数 for(int i=1;i<=n;++i) { int a,b,c; cin>>a>>b>>c; int k=1; while(k<=c) { cnt++; v[cnt]=a*k; w[cnt]=b*k; c-=k; k*=2; } if(c) { cnt++; v[cnt]=a*c; w[cnt]=b*c; } } n=cnt;//总物品的数量变为cnt个 for(int i=1;i<=n;++i) for(int j=m;j>=v[i];j--) f[j]=max(f[j],f[j-v[i]]+w[i]); cout<



