题目来源:https://leetcode.cn/problems/ones-and-zeroes/description/
C++题解:本题其实是01背包问题!只不过这个背包有两个维度,一个是m 一个是n,而不同长度的字符串就是不同大小的待装物品。动规五部曲:
- 确定dp数组(dp table)以及下标的含义。dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
- 确定递推公式。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);
- dp数组如何初始化。因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
- 确定遍历顺序。01背包外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。
- 举例推导dp数组。
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int len = strs.size();
// strs每个元素的01个数情况
vector<int> zs(len, 0), os(len, 0);
for(int i = 0; i < len; i++){
int chang = strs[i].size();
for(int j = 0; j < chang; j++){
if(strs[i][j] == '0') zs[i]++;
else os[i]++;
}
}
// 二维动态数组,从后往前遍历
// dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for(int i = 0; i < len; i++) {
for(int j = m; j >= zs[i]; j--) {
for(int k = n; k >= os[i]; k--) {
dp[j][k] = max(dp[j][k], dp[j-zs[i]][k-os[i]] + 1);
}
}
}
return dp[m][n];
}
};
代码随想录代码:
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
for (string str : strs) { // 遍历物品
int oneNum = 0, zeroNum = 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];
}
};