1.01背包:有n种物品与承重为m的背包。每种物品只有一件,每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大。
板子: (这里用到了滚动数组,空间优化)
for(int i = 1;i <= n;i ++) //n种物品 { for(int j = bag;j >= w[i];j --) //因为你要用到上一状态的值 所有必须从后向前遍历 { dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } }
2.完全背包:有n种物品与承重为m的背包。每种物品有无限多件,每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大。
板子:此时与01的区别在于 数量无限 第二循环正着遍历的意义在于每次取不同个数的物品找最优
for(int i = 1;i <= n;i ++) //n种物品 { for(int j = w[i];j <= bag;j ++) dp[j] = max(dp[j], dp[j - w[i]] + v[i]); }
3.多重背包:有n种物品与承重为m的背包。每种物品有有限件num[i],每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大。
板子:和01背包类似 多了一层遍历物品个数的循环
当然 数据 时间要求严格时 需要用二进制枚举
for(int i = 1;i <= n;i ++) //n种物品 { for(int j = 0;j < num[i];j ++) //对每种物品的个数开始遍历 (这是在数据很小的情况下 数据大的时候用二进制枚举) { for(int k = bag;k >= w[i];k --) // 写法如 01背包 { dp[k] = max(dp[k], dp[k - w[i]] + v[i]); } } }
下面是多重背包的一道应用题
hdu2844 点击打开链接
当 价值与个数的乘积大于 容量m时 这时 我们可以把它当着 用不完的数量 即看作 完全背包
相反 小于m时 则当作 01背包处理 然后 利用二进制枚举
#include<bits/stdc++.h> using namespace std; int m, n; int v[105],num[105]; int dp[100005]; int main() { while(cin >> n >> m && n && m) { fill(dp,dp + 100005, -99999999); dp[0] = 1; //面值为0的时候只要一种选择 即 不掏钱 for(int i = 1;i <= n;i ++) { cin >> v[i]; } for(int i = 1;i <= n;i ++) { cin >> num[i]; } for(int i = 1;i <= n;i ++) { //按完全背包算 if(num[i] * v[i] >= m) { for(int j = v[i];j <= m;j ++) dp[j] = max(dp[j], dp[j - v[i]] + v[i]); } else { for(int j = 1;j <= num[i];j = j*2) { for(int k = m;k >= v[i] * j;k --) { dp[k] = max(dp[k], dp[k - v[i] * j] + v[i] * j); } num[i] -= j; } if(num[i] > 0) { for(int j = m;j >= v[i] * num[i];j --) dp[j] = max(dp[j], dp[j - v[i] * num[i]] + v[i] * num[i]); } } } int cnt = 0; for(int i = 1;i <= m;i ++) { if(dp[i] > 0) //i表示面值 如果说面值为i时的值不为0 则 说明此时有组成i的解 cnt ++; } cout << cnt << endl; } return 0; }