0-1背包问题
目录
- 描述
- 方法:动态规划
题目来源:01背包 | 牛客网
描述
已知一个背包最多能容纳体积之和为v的物品。现有 n 个物品,第 i 个物品的体积为 vi , 重量为 wi。求当前背包最多能装多大重量的物品?
数据范围: 1≤v≤1000,1≤n≤1000,1≤n≤1000 , 1≤vi≤1000, 1≤wi≤1000
进阶:O(n?v)
方法:动态规划
每个物品只有2种状态:放入(0),不放入(1),也因此称为01背包问题。
假设第i个阶段表示处理第i个物品,第i-1个阶段表示处理第i-1个物品,则当处理第i个物品时,前i-1个物品已处理完毕,只需要考虑第i-1阶段向第i阶段的转移。
状态表示:c[i][j] 表示将前i种物品放入体积为j的背包中,获得的最大重量。
第i个物品处理状态 2种:
- 不放入:放入背包的重量不增加(跟第i-1个物品一样),c[i][j] = c[i-1][j]。
- 放入:问题可转化为,在第i-1阶段向第i阶段转化。因为第i阶段已经放入体积v[i](重量w[i]),所以第i-1阶段体积j-v[i],此时可以知道c[i][j] = c[i-1][j-v[i]] + w[i]
特殊情况:背包体积不够,则肯定不能放入物品,有c[i][j] = c[i-1][j],j < v[i]
因此,可以得到状态转移方程:
当j < v[i]时,c[i][j] = c[i-1][j]
当j >= v[i]时,c[i][j] = max(c[i-1][j], c[i-1][j-v[i]] + w[i])
而初始状态:
c[0..n][j] = 0,c[i][0..V] = 0
因此,可以通过状态转移方程得到所有c[i][j]值,而c[n][V]就是所求。
代码:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算01背包问题的结果
* @param V int整型 背包的体积
* @param n int整型 物品的个数
* @param vw int整型vector> 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
* @return int整型
*/
int knapsack(int V, int n, vector >& vw) {
// write code here
// assert(V >= 1 && V <= 1000);
// assert(n >= 1 && n == vw.size());
// assert(vw[0].size() == 2);
vector > c(n + 1, vector(V + 1, 0)); // 全部初始化为0
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= V; ++j) {
int idx = i - 1;
if (j < vw[idx][0]) {
c[i][j] = c[i - 1][j];
}
else {
c[i][j] = std::max(c[i - 1][j], c[i - 1][j - vw[idx][0]] + vw[idx][1]);
}
}
}
return c[n][V];
}