给你一个二进制字符串数组 strs
和两个整数 m
和 n
。
请你找出并返回 strs
的最大子集的长度,该子集中 最多 有 m
个 0
和 n
个 1
。
如果 x
的所有元素也是 y
的元素,集合 x
是集合 y
的 子集 。
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 str : strs){//遍历物品
int zeroNum = 0, oneNum = 0;
for (char c : str){
if (c == '0')
zeroNum++;
else
oneNum++;
}
for (int i = m; i >= zeroNum; i--){
for (int j = n; j >= oneNum; j--){
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
};
开始动规五部曲:
1.确定dp数组以及下标的含义
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
2.确定递推公式
dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的一个字符串有zeroNum个0,oneNum个1。
dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
然后我们在遍历的过程中,取dp[i][j]的最大值。
所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。
这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。
3.dp数组如何初始化
物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
4.确定遍历顺序
01背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
本题是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。
遍历背包容量的两层for循环先后循序没有讲究,都是物品重量的一个维度,先遍历哪个都行!
5.举例推导dp数组
以输入:["10","0001","111001","1","0"],m = 3,n = 3为例
最后dp数组的状态如下所示:
总结
0-1背包的多种应用:
- 纯 0 - 1 背包 (opens new window)是求 给定背包容量 装满背包 的最大价值是多少。
- 416. 分割等和子集 (opens new window)是求 给定背包容量,能不能装满这个背包。
- 1049. 最后一块石头的重量 II (opens new window)是求 给定背包容量,尽可能装,最多能装多少
- 494. 目标和 (opens new window)是求 给定背包容量,装满背包有多少种方法。
- 本题是求 给定背包容量,装满背包最多有多少个物品。