HDU2844买表多重背包问题
题目大意都不大好懂,是利用手头上的硬币看看能组合出多少种价格,也就是跑完背包,看看有多少背包符合要求
剩下的就是多重背包的问题了
1.第一个处理办法就是直接当01背包进行存储和处理
2.第二个处理办法就是扫描物品时,当这个物品和数量产生的总价格大于限制时,采用完全背包的方法往里放不大的时候就按01背包的方法放
因为对于每一件物品我们所考虑的问题就是,放不放和放多少的问题
其实这两个方法都一样,只不过第一个方法啊直观上的解法就是扩充数组而第二个的直观解法节约了空间
学习和理解以下代码的时候,我有两个地方不好懂
1.二进制压缩原理:
我们都知道背包是一层一层向上构建的
只要抓住 1 2 4 8 16 32 .... 2^n这些数字可以合成 1到2^(n-1)-1中任何数字
简单来说:基础是0,第一次我取一个,dp更新的也只是取 1 个的基础
第二次我取两个,dp数组更新的就会是1个,2 个和3 个(二的基础加一的基础)的基础了
以此类推为了防止越出基础范围,所以每次要减去k,看看k还能不能再增加~
2.第二个就是最后符合条件的为什么是大于等于0就可以
这里跟题目理解的有些关系,对于背包的每一个容量,都必须完全装满才可以
所以这也是为什么对dp数组初始化的时候只把基础dp[0]初始化为0,其余的都初始化为相对无穷小
这样才能层层递推,而且递推出来的都是装的满满的背包
例如输入
2 5
1 4 2 1
那么第一次更新为01背包,更新了数组dp[1]
第二次也是01,更新了数组dp[2]
第三次01,更新了dp[4]和dp[5]
#include <iostream> #include <cstdio> #include <string.h> #define inf 0xffffff using namespace std; const int maxn = 1e6; const int maxm = 200; int a[maxm],c[maxm]; int dp[maxn]; void Zero_one_pack(int m,int v,int w) { for(int i = m;i >= v;i--) dp[i] = max(dp[i],dp[i-v]+w); // cout<<"dp2 = "<<dp[5]<<endl; } void Complete_pack(int m,int v,int w) { for(int i = v;i <= m;i++) dp[i] = max(dp[i],dp[i-v]+w); } void multiplePack(int m,int v,int w,int num) { if(v * num >= m) { Complete_pack(m,v,w); return; } int k = 1; for(k;k <= num;k <<= 1) { Zero_one_pack(m,k*v,k*w); num -= k; } if(num) Zero_one_pack(m,num*v,num*w); } int main() { int n,m; while(~scanf("%d %d",&n,&m)) { if(n == 0 && m == 0)break; for(int i = 0;i < n;i++) scanf("%d",&a[i]); for(int i = 0;i < n;i++) scanf("%d",&c[i]); for(int i = 1;i <= m;i++) dp[i] = -inf; //背包必须要装满,所以初始赋值为负无穷 //对0赋值为0,层层向上递推 dp[0] = 0; for(int i = 0;i < n;i++) { multiplePack(m,a[i],a[i],c[i]); } int sum = 0; for(int i = 1;i <= m;i++) { if(dp[i] > 0) { sum++; } } cout<<sum<<endl; } return 0; }