背包九讲
01背包-牛客网
已知一个背包最多能容纳体积之和为V的物品,现有 n 个物品,第 i 个物品的体积为 vi , 重量为 wi,求当前背包最多能装多大重量的物品?
使用动态规划来解决01背包问题:
首先定义dp数组:dp[i][j] := 0-i号物品面对容量为j的背包所能获得的最大重量
状态转移方程:
dp[i][j]={dp[i−1][j],容量不足,不放入物品imax(dp[i−1][j],dp[i−1][j−v[i]]+w[i]),考虑拿这件物品能否获得更大重量dp[i][j]= \begin{cases} dp[i-1][j],\quad 容量不足,不放入物品i\\ max(dp[i - 1][j], dp[i - 1][ j - v[i]] + w[i]), \quad 考虑拿这件物品能否获得更大重量 \end{cases} dp[i][j]={dp[i−1][j],容量不足,不放入物品imax(dp[i−1][j],dp[i−1][j−v[i]]+w[i]),考虑拿这件物品能否获得更大重量
class Solution {
public:int knapsack(int V, int n, vector >& vw) {vector> dp(n + 1, vector(V + 1, 0));for (int i = 1; i <= n; ++i) {for (int j = 1; j <= V; ++j) {if (j < vw[i - 1][0]) {dp[i][j] = dp[i - 1][j];} else {dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - vw[i - 1][0]] + vw[i - 1][1]);}}}return dp[n][V];}
};
由于dp问题具有无后效性,可以采用滚动数组的方式节省内存,此时状态转移方程化为:
if j >= v[i]:dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
Charm Bracelet:定义一维的dp数组,每次从后向前刷新dp数组:
#include
#include
using namespace std;int main() {int N, M;cin >> N >> M;vector W(N, 0);vector D(N, 0);vector dp(M + 1, 0);for (int i = 0; i < N; ++i) {cin >> W[i] >> D[i];}for (int i = 0; i < N; ++i) {for (int j = M; j >= 1; --j) {if (j >= W[i]) {dp[j] = max(dp[j], dp[j - W[i]] + D[i]);}}}cout << dp[M] << endl;return 0;
}
完全背包-牛客网
你有一个背包,最多能容纳的体积是V。现在有n种物品,每种物品有任意多个,第 i 种物品的体积为vi,价值为wi,求这个背包至多能装多大价值的物品?
完全背包的状态转移方程和01背包极其相似:
dp[i][j]={dp[i−1][j],容量不足,不放入物品imax(dp[i−1][j],dp[i][j−v[i]]+w[i]),考虑拿这件物品能否获得更大重量dp[i][j]= \begin{cases} dp[i-1][j],\quad 容量不足,不放入物品i\\ max(dp[i - 1][j], dp[i][ j - v[i]] + w[i]), \quad 考虑拿这件物品能否获得更大重量 \end{cases} dp[i][j]={dp[i−1][j],容量不足,不放入物品imax(dp[i−1][j],dp[i][j−v[i]]+w[i]),考虑拿这件物品能否获得更大重量
而且,考虑使用滚动数组压缩空间,完全背包和01背包的状态转移方程是一样的。区别在于dp数组的遍历方式:完全背包问题必须顺序推导dp数组,而01背包采用逆序推导dp数组
if j >= v[i]:dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
class Solution {public:vector knapsack(int v, int n, vector >& nums) {// write code herevector res;vector dp(v + 1, 0);vector pack(v + 1, -255);pack[0] = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j <= v; ++j) {if (j >= nums[i][0]){dp[j] = max(dp[j], dp[j - nums[i][0]] + nums[i][1]);pack[j] = max(pack[j], pack[j - nums[i][0]] + nums[i][1]); //只考虑能从0一步步跳到v的w}}}res.push_back(dp[v]);if (pack[v] > 0) {res.push_back(pack[v]);} else {res.push_back(0);}return res;}
};
Piggy-Bank
与上面的两种背包问题不同,分数背包问题(物品可以被任意分割)比较简单,可以用贪心算法解决:每次都选择单位价值最大的物品装入背包,如果未满,则选择下一个价值次大的物品装入背包
圣诞老人的礼物-百练
#include
#include
#include
using namespace std;#define MAXN (100+10)struct Box {int v;int w;double density;Box() {}Box(int vv, int ww) :v(vv), w(ww), density(double(v) / w) {}bool operator<(const Box& b) {return density < b.density;}
};int n, weigth;
double total_w = 0;
double total_v = 0;
Box boxes[MAXN];int main() {cin >> n >> weigth;int v, w;for (int i = 0; i < n; ++i) {cin >> v >> w;boxes[i] = Box(v, w);}sort(boxes, boxes + n);for (int i = n - 1; i >= 0; --i) {if (total_w + boxes[i].w < weigth) {total_w += boxes[i].w;total_v += boxes[i].v;}else {total_v += boxes[i].density * (weigth - total_w);total_w = weigth;}}printf("%.1f\n", total_v);return 0;
}