leeetcode474. 一和零(二维01背包)
链接:https://leetcode-cn.com/problems/ones-and-zeroes/
题目
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例
示例 1:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例 2:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 '0' 和 '1' 组成
1 <= m, n <= 100
思路
这题是标准的二维0-1背包模板题,
在做的时候,我没考虑到,直接按照一维背包问题用贪心模拟了两个维度的背包
以0作为放入背包的物体,然后判断放入背包时1的数量,如果0的数量相同且无法放入更多的0,则判定1最少情况
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int sn = strs.size();
vector<vector<int>>v(sn, vector<int>(2));
for (int i = 0; i<sn; i++){
int cnt0 = 0, cnt1 = 0;
for (auto s : strs[i]){
if (s == '1')
cnt1++;
if (s == '0')
cnt0++;
}
v[i][0] = cnt0;
v[i][1] = cnt1;
}
sort(v.begin(), v.end(), [](vector<int>&a, vector<int>&b){return a[1] < b[1]; });
vector<vector<int>>dp0(m+1 ,vector<int>(2));
priority_queue<int>pq;
int ans = 0;
for (int i = 0; i < sn; i++){
vector<vector<int>>t0 = dp0;
if (v[i][0] < m + 1 && v[i][1] < n + 1){
if (!dp0[v[i][0]][0]){
dp0[v[i][0]][0] = 1;
dp0[v[i][0]][1] = v[i][1];
}
else if (dp0[v[i][0]][0] == 1&&v[i][0]!=0){
dp0[v[i][0]][1] = min(dp0[v[i][0]][1], v[i][1]);
}
else if (v[i][0] == 0){
if (dp0[0][1] + v[i][1] < n + 1){
dp0[0][0] = dp0[0][0] + 1;
dp0[0][1] = dp0[0][1] + v[i][1];
pq.push(v[i][1]);
}else{
if (pq.top() > v[i][1]){
dp0[0][1] = dp0[0][1] - pq.top();
pq.pop();
dp0[0][1] = dp0[0][1] + v[i][1];
pq.push(v[i][1]);
}
}
}
ans = max(dp0[v[i][0]][0], ans);
}
for (int j = v[i][0]; j < m + 1;j++){
if (t0[j - v[i][0]][0]){
if (t0[j - v[i][0]][0] + 1 > dp0[j][0] && t0[j - v[i][0]][1] + v[i][1] < n + 1){
dp0[j][0] = t0[j - v[i][0]][0] + 1;
dp0[j][1] = t0[j - v[i][0]][1] + v[i][1];
}
else if (t0[j - v[i][0]][0] + 1 == dp0[j][0]){
dp0[j][1] = min(t0[j - v[i][0]][1] + v[i][1], dp0[j][1]);
}
}
ans = max(ans, dp0[j][0]);
}
}
return ans;
}
};
但实际上,只需要将将一维背包问题扩展到二维,转移方程为dp[i][j]=max(dp[i][j],dp[i-c0][j-c1]+1);
注意使用了空间压缩后的二维背包,需要从后往前进行遍历,由于需要用到前置背包的数据,所以只能先修改前部分
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>>dp(m+1,vector<int>(n+1));
for(auto &str:strs){
int c0=count(str.begin(),str.end(),'0'),c1=count(str.begin(),str.end(),'1');
for(int i=m;i>=c0;--i){
for(int j=n;j>=c1;--j){
dp[i][j]=max(dp[i][j],dp[i-c0][j-c1]+1);
}
}
}
return dp[m][n];
}
};