题目链接
代码随想录文章讲解链接
用时:43m57s
相比于之前的01背包问题多了一个维度,之前的背包是只有一个容量限制,本题有两个容量限制,分别是0的容量限制和1的容量限制。
dp数组:dp[j][k]
表示:对于0到i的字符串,0的容量为j、1的容量为k的背包能装的字符串的最大数量
状态转移方程:dp[j][k] = max(dp[j][k], dp[j - num0][k - num1] + 1)
,当前字符串要么装要么不装。
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (string& s : strs) {
// 统计当前字符串中0和1的数量
int num0 = 0, num1 = 0;
for (char& c : s) {
if (c == '0') ++num0;
else ++num1;
}
// dp
for (int j = m; j >= num0; -- j) {
for (int k = n; k >= num1; --k) {
dp[j][k] = max(dp[j][k], dp[j - num0][k - num1] + 1);
}
}
}
return dp[m][n];
}
};
多维度的01背包问题,一开始是想把多个维度拆分成多个01背包,还是搞复杂了。
无。
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解物品装入背包里的最大价值。
示例:
输入:w = 4, weight = [1, 3, 4], value = [15, 20, 30]
输出:60
解释:放入4个索引为0的物品,最大价值为15*4=60。
代码随想录文章讲解链接
dp数组:二维dp数组,dp[i][j]表示对于物品0到i,容量为j的背包可以装的最大价值
状态转移:dp[i][j] = max(dp[i][j - 1], dp[i][j - weight[i]] + value[i])
,注意关键在于max函数的第二项,在01背包中是为dp[i - 1][j - weight[i]] + value[i]
,因为01背包不能重复放,所以是i - 1,而完全背包可以重复放,所以是i
class Solution {
public:
int completePack(int w, vector<int>& weight, vector<int>& value) {
int size = weight.size();
vector<vector<int>> dp(size, vector<int>(w + 1, 0));
// 初始化dp数组
for (int j = 1; j <= w; ++j) {
if (weight[0] <= j) dp[0][j] = max(dp[0][j - 1], dp[0][j - weight[0]] + value[0]);
}
// dp
for (int i = 1; i < size; ++i) {
for (int j = 1; j <= w; ++j) {
if (weight[i] > j) dp[i][j] = dp[i - 1][j]; // 如果物品i的重量大于j,那么物品i肯定不放,所以dp[i][j]就等于dp[i - 1][j]
else dp[i][j] = max(dp[i][j - 1], dp[i][j - weight[i]] + value[i]);
}
}
return dp[size - 1][w];
}
};
关键在于正序遍历,正序遍历相当于同一个物品可以放置多次。
int completePack(int w, vector<int>& weight, vector<int>& value) {
int size = weight.size();
vector<int> dp(w + 1, 0);
for (int i = 0; i < size; ++i) {
for (int j = weight[i]; j <= w; ++j) {
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
return dp[w];
}
无。
无。
题目链接
代码随想录文章讲解链接
用时:21m26s
dp数组:二维dp数组,dp[i][j]
表示使用硬币0到i组合成数额j的组合数量
状态转移:如果硬币i大于数额j,那么硬币i无法使用,组合数量就相当于不使用硬币i的组合数量dp[i][j] = dp[i - 1][j]
。如果硬币i小于等于数额j,硬币i可以使用,组合数量等于不使用硬币i的组合数加上使用了硬币i的组合数,不使用硬币i的组合数为dp[i - 1][j]
,使用硬币i的组合数为dp[i][j - coins[i]]
,则dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]
。
class Solution {
public:
int change(int amount, vector<int>& coins) {
int size = coins.size();
vector<vector<int>> dp(size, vector<int>(amount + 1, 0));
// 初始化dp数组
for (int i = 0; i < size; ++i) dp[i][0] = 1;
for (int j = 1; j <= amount; ++j) {
if (coins[0] <= j) dp[0][j] = dp[0][j - coins[0]];
}
// dp
for (int i = 1; i < size; ++i) {
for (int j = 1; j <= amount; ++j) {
if (coins[i] > j) dp[i][j] = dp[i - 1][j];
else dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
}
}
return dp[size - 1][amount];
}
};
用时:1m30s
class Solution {
public:
int change(int amount, vector<int>& coins) {
int size = coins.size();
vector<int> dp(amount + 1, 0);
// 初始化dp数组
dp[0] = 1;
// dp
for (int i = 0; i < size; ++i) {
for (int j = coins[i]; j <= amount; ++j) dp[j] = dp[j] + dp[j - coins[i]];
}
return dp[amount];
}
};
无。
无。
题目链接
代码随想录文章讲解链接
用时:14m30s
**关键点:**外层循环是遍历背包容量,内层循环是遍历数组。
内层循环遍历数组,这样就能计算上以每个元素为开头的排列数。
如果外层遍历数组,内层遍历背包,这样每次只能计算以nums[i]作为开头的排列数。
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int size = nums.size();
vector<int> dp(target + 1, 0);
dp[0] = 1;
for (int j = 1; j <= target; ++j) {
for (int i = 0; i < size; ++i) {
if (nums[i] <= j && dp[j - nums[i]] < INT_MAX - dp[j]) dp[j] += dp[j - nums[i]];
}
}
return dp[target];
}
};
完全背包中,遍历的顺序会导致不同的结果。
无。
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 [1, 2, …, n] 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
示例 2:
输入: 3
输出: 4
解释: 有四种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
3 阶
代码随想录文章讲解链接
class Solution {
public:
int climbStairs(int n) {
vector<int> dp(n + 1, 0);
dp[0] = 1;
for (int j = 0; j <= n; ++j) {
for (int i = 1; i <= j; ++i) dp[j] += dp[j - i];
}
return dp[n];
}
};
无。
无。
你的背包~ 背到现在还没烂~
背包问题还是有难度的,搞懂了01背包,又来完全背包。