1. 题目描述
有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值。已知对于一件物品必须选择取(用1表示)或者不取(用0表示),且每件物品只能被取一次(这就是“0-1”的含义)。 求放置哪些物品进背包,可使这些物品的重量总和不超过背包容量,且价值总和最大。
样例 1:
输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
输出: 9
解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9
样例 2:
输入: m = 10, A = [2, 3, 8], V = [2, 5, 8]
输出: 10
解释: 装入 A[0] 和 A[2] 可以得到最大价值, V[0] + V[2] = 10
2. 思路分析
可以先这样考虑,当背包容量为1时,如何放置物品才能使背包中价值最大;同样当背包容量为2时,如何放置能使背包中价值最大,以此类推,直到背包容量为10。此时我们需要维护一张二维表max_value[i][j],其中横坐标i表示物品,纵坐标表示背包容量(0<=j<=10)。
状态:
F(i, j): 前i个物品放入大小为j的背包中所获得的最大价值
状态递推:
对于第i个商品,有一种例外,装不下,两种选择,放或者不放
如果装不下:此时的价值与前i-1个的价值是一样的F(i,j) = F(i-1,j)
如果可以装入:需要在两种选择中找最大的F(i, j) = max{F(i-1,j), F(i-1, j - A[i]) + V[i]}
F(i-1,j): 表示不把第i个物品放入背包中, 所以它的价值就是前i-1个物品放入大小为j的背包的最大价值。
F(i-1, j - A[i]) + V[i]:表示把第i个物品放入背包中,价值增加V[i],但是需要腾出j - A[i]的大小放第i个商品。
初始化:
第0行和第0列都为0,表示没有装物品时的价值都为0。F(0,j) = F(i,0) = 0
返回值:
F(n,m)
3. 实例
对于承重为m=5的背包有:
根据上面的思路可以得到下面这个二维递推表:
因此最优解F(4,5)=37.
4. 代码
//多增加一行一列:行为0表示没有物品乐园放入 列为0表示背包没有大小 所有的物品都不能放入
class Solution {
public:
int backPackII(int m, vector<int> &A, vector<int> &V) {
// write your code here
if (A.empty() || V.empty() || m <0)
{
return 0;
}
int row = A.size() + 1;//行数
//列数:m+1-----因为增加了0列
int col = m + 1;
vector<vector<int>>max_value(row, vector<int>(col));
for (int j = 0; j < col; ++j)
{
max_value[0][j] = 0;
}
for (int i = 1; i < row; ++i)
{
max_value[i][0] = 0;
}
for (int k = 1; k < row; ++k)
{
for (int l = 1; l < col; ++l)
{
//第k个商品在A中对应的索引为k-1: k从1开始
//如果第k个商品大于l,说明放不下, 所以(k,l)的最大价值和(k-1,l)相同
if (l<A[k - 1])//放不进去
{
max_value[k][l] = max_value[k - 1][l];
}
else
{
//如果可以装下,分两种情况,装或者不装
//如果不装,则即为(k-1, l)
//如果装,需要腾出放第k个物品大小的空间: l - A[k-1],装入之后的最大价值即为(k - 1, l - A[k - 1]) + 第k个商品的价值V[k - 1]
//最后在装与不装中选出最大的价值
max_value[k][l] = max(max_value[k - 1][l], (max_value[k - 1][l - A[k - 1]] + V[k - 1]));
}
}
}
return max_value[row - 1][col - 1];
}
};