Backpack
题目描述
在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]
样例 1:
输入: [3,4,8,5], backpack size=10
输出: 9
样例 2:
输入: [2,3,5,7], backpack size=12
输出: 12
解题思路
这里使用动态规划的思想,动态规划四部曲
- 状态:F(i,j) 将第i个物品装入体积为j的的背包中
- 递推方程:
分两种情况:1.第i个物品的大小大于当前背包能装入物品的大小,放不下,
此时第i个物品最大大小与放入第i - 1物品时相同 F(i,j) = F(i-1,j)
2.第i个物品大小小于当前背包能装入物品大小,此时又分为
两种情况:1.放的下不放该物品,此时和第一种情况一样F(i,j) = F(i-1,j)
2.放的下且放进去,此时放入第i个物品的重量等于背包减去第i件物品对应大小对应位置上的背包装入的最大重量加上第i件物品的重量 F(i,j) = F(i - 1,j - A[i] + A[i]), 此时必须取上述两种情况中大的一个 max(F(i-1,j),F(i-1,j-A[i]+A[i])) - 初始化:
第1行和第1列的值都为0,因为未放置物品F(i,0) = F(0,j) = 0 - 返回值:
返回将n个物品放入大小为m的背包中 F[n][m]
但是我们想一想,在求第i件物品装入之后所得最大的重量时我们用到的只有放入第 i-1 件时所得到的重量,所以我们这时优化一下我们代码的空间复杂度,只开辟一维数组的空间,就可以解决上述问题,但是这里必须注意的是,我们在更新的时候必须从后往前更新这个数组,因为我们要用到之前的数据,避免被新数据覆盖。
代码实现
class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @return: The maximum size
*/
int backPack(int m, vector<int> &A) {
// write your code here
if (m < 1 || A.empty())
return 0;
int N = A.size();
//多开辟一列,方便初始化第一列的值为0
int M = m + 1;
vector<int> Fv(M, 0);
for (int i = 0; i < N; i++)
{
//从后往前更新,否则会覆盖之前的数据
for (int j = M - 1; j > 0; j--)
{
//若第i件物品若能放进去,找放进去和不放进去中质量更大的那个
//放进去,就是减去第i件物品大小之后,在Fv中对应位置的质量加上第i件物品的重量
//不放进去就是一维数组该位置的质量
if (A[i] < j)
Fv[j] = max(Fv[j], Fv[j - A[i]] + A[i]);
else
//放不进去就是该位置的质量
Fv[j] = Fv[j];
}
}
return Fv[m];
}
};
题目链接:Backpack
BackPackII
题目描述
有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
问最多能装入背包的总价值是多大?
样例 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
解题思路
动态规划:
- 状态:F(i,j) 将第i个物品装入体积为j的的背包中
- 递推方程:
分两种情况:1.第i个物品的大小大于当前背包能装入物品的大小,放不下,
此时第i个物品最大价值与放入第i - 1物品时相同 F(i,j) = F(i-1,j)
2.第i个物品大小小于当前背包能装入物品大小,此时又分为
两种情况:1.放的下不放该物品,此时和第一种情况一样F(i,j) = F(i-1,j)
2.放的下且放进去,此时放入第i个物品的价值等于减去i对应大小后对应位置上的价值加上第i个物品对应的价值,F(i,j) = F(i - 1,j - A[i] + V[i]),此时必须取上述两种情况中大的一个 max(F(i-1,j),F(i-1,j-A[i]+V[i])) - 初始化:
第1行和第1列的值都为0,因为未放置物品F(i,0) = F(0,j) = 0 - 返回值:
返回将n个物品放入大小为m的背包中 F[n][m]
代码实现
class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @param V: Given n items with value V[i]
* @return: The maximum value
*/
int backPackII(int m, vector<int> &A, vector<int> &V) {
// write your code here
if (A.empty() || V.empty() || m < 1)
return 0;
//增加一行一列以方便初始化
int N = A.size() + 1;
int M = m + 1;
//将所有值先初始化为0,包括第一行第一列
vector<vector<int>> Fv(N, vector<int>(M, 0));
for (int i = 1; i < N; ++i)
{
for (int j = 1; j < M; ++j)
{
//当第i个物品装不下背包时,价值与之前的相等
if (A[i - 1] > j)
Fv[i][j] = Fv[i - 1][j];
//装的下时,在装不装中选取更大的值
else
{
//如果能够放进背包,那就在放进去和不放进去中找一个价值更大的一个
//不放进去价值和之前该位置的值相等
//放进去,等于减去i对应大小后对应位置上的价值加上第i个物品对应的价值
Fv[i][j] = max(Fv[i - 1][j], Fv[i - 1][j - A[i - 1]] + V[i - 1]);
}
}
}
return Fv[N - 1][m];
}
};
优化BackPack II
思路:
将第i个物品装入体积为j的的背包中
由于我们求上述问题时只需要去得放第i-1个物品时取得的最大价值
因此我们将二维空间优化为一维空间,必须从前往后更新这个一维数组,
否则在使用前面数据时之前的就已经被覆盖
class Solution {
public:
int backPackII(int m, vector<int> &A, vector<int> &V) {
// write your code here
if (A.empty() || V.empty() || m < 1)
return 0;
int N = A.size();
int M = m + 1;
vector<int> Fv(M, 0);
for (int i = 0; i < N; ++i)
{
for (int j = M - 1; j > 0; j--)
{
//如果第i个物品放不进背包,价值与之前一维数组中该位置的值相等
if (A[i] > j)
Fv[j] = Fv[j];
else
{
//如果能够放进背包,那就在放进去和不放进去中找一个价值更大的一个
//不放进去价值和之前该位置的值相等
//放进去,等于减去i对应大小后对应位置上的价值加上第i个物品对应的价值
Fv[j] = max(Fv[j], Fv[j - A[i]] + V[i]);
}
}
}
return Fv[m];
}