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];

    }
};
posted @ 2021-12-20 16:48  kitamu  阅读(34)  评论(0编辑  收藏  举报
Live2D