【温故知新】`474. 一和零` 01背包🎒问题最大解-动态规划实现

636 阅读3分钟

题目描述

给你一个二进制字符串数组 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

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/on… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

image.png

01背包问题🎒;

01背包🎒问题的特点是,每一件物品只有一个,其选择放入背包或者选择不放入背包🎒; 题目理解:本题中strs中的每一个元素都作为一个商品;而背包🎒只有一个且容量为m和n两个维度同时满足条件下的最大容量; 因此本题为01背包问题最大解类型; 动态规划四部曲:

  • 确定dp含义:
    • dp[i][j]表示满足最多i个0且j个1的strs的最大子集
  • 确定递推公式:
    • 我们假设dp[i][j]的最近的一次+1的strs数组元素为substr,substr此时满足有x个0,y个1;
    • dp[i][j]可以由dp[i-x][j-y]+1推导而来;
    • 然后将上次推导的结果dp[i][j]与dp[i-m][j-n]+1进行比较;取最大值;
    • 递推公式为:dp[i][j]=Math.max(dp[i][j],p[i-m][j-n]+1);
    • 回想一下01背包问题的递归公式为dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i])
    • 字符串substr的x和y相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。
  • dp数组初始化;
    • 01背包问题🎒,物品价值不会是负数,将dp数组初始化为0即可;用于递推基础;
  • 遍历顺序:
    • 01背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!

代码

/**
 * @param {string[]} strs
 * @param {number} m
 * @param {number} n
 * @return {number}
 */
var findMaxForm = function(strs, m, n) {
    // 这是一道变相的01背包问题,选择或者不选择放入背包🎒
    // 定义dp:dp[i][j]表示最多由i个0和j个1的子集个数;
        // 此时strs中的元素相当于是物品,背包🎒容量为有i和j两个维度同时满足的作为条件;
        // 确定01背包问题🎒递推公式 dp[j]=Math.max(dp[j],dp[j-wight[i]]+value[i])
        // 所以此时递推公式应该为:dp[i][j]=Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1)
        
        // 1、统计每一个字符串元素(物品)的维度i、j容量;
        let map=new Map();
        for(let item of strs){
            let oneNum=0;
            for(let char of item){
                if(char==='1'){
                    oneNum++
                }
            }// end of for
            map.set(item,[item.length-oneNum,oneNum])
        }
        // 2、完成i和j两个维度容量的统计,构建dp二维数组,并进行初始化0;
        const dp=new Array(m+1)
        for(let i=0;i<(m+1);i++){
            dp[i]=new Array(n+1).fill(0);
        }
        // 3、先遍历商品,再遍历容量;
        const N=strs.length;
        // 遍历商品
        for(let i=0;i<N;i++){
            // 遍历容量m、n两个维度作为容量条件;
            let [v0,v1]=map.get(strs[i])
            for(let j=m;j>=v0;j--){
                for(let k=n;k>=v1;k--){
                    dp[j][k]=Math.max(dp[j][k],dp[j-v0][k-v1]+1);
                }
            }// end of m n
        }// end of for
        return dp[m][n]
};

完全背包🎒问题

【温故知新】322. 零钱兑换 动画演示-完全背包问题最小解-动态规划实现

【温故知新】518. 零钱兑换 II 完全背包问题组合解-动态规划实现

【温故知新】377. 组合总和 Ⅳ 完全背包问题排列解-动态规划实现