1. 程式人生 > 其它 >leeetcode474. 一和零(二維01揹包)

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

    }
};