Leetcode 474 一和零【动态规划01背包问题】

Leetcode 474 一和零

中等题 属于动态规划中的01背包问题,但是这里比较特殊,由多层背包组成;

题目

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例1

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5031 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"}{"10","1","0"}{"111001"} 不满足题意,因为它含 41 ,大于 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

解法

理清楚题目的含义,是说在数组strs中挑选元素组成集合,使得集合内的元素0,1的个数满足“最多 有 m 个 0 和 n 个 1 ”这个条件;问这个集合的元素最多有多少个。

在有两个限制条件的基础上,挑选有限个元素,这类问题是动态规划中的0-1背包问题,并且由于存在两个条件,所以是多重0-1背包问题;

传统01背包

  • 定义dp数组含义:我们将dp[i][j][k]定义为在遍历到第i个元素时,‘0’的空间有j个,‘1’的空间有k个的情况下,满足子集的最大长度。
  • dp的初始化和边界问题,dp的大小为 ( l e n ( s t r s ) + 1 ) × m × n (len(strs)+1) \times m \times n (len(strs)+1)×m×n,在处理第0个元素或者j,k为0时,dp也应该为0,所以直接将dp初始化为0
  • 转移状态方程
    x,y表示当前元素字符串中的0,1数字的个数;
    d p [ i ] [ j ] [ k ] = { m a x ( d p [ i − 1 ] [ j ] [ k ] , d p [ i − 1 ] [ j − x ] [ k − y ] + 1 ) x<=j and y<=k d p [ i − 1 ] [ j ] [ k ] o t h e r dp[i][j][k] = \begin{cases} max(dp[i-1][j][k],dp[i-1][j-x][k-y]+1) &\text{x<=j and y<=k} \\ dp[i-1][j][k] &other \end{cases} dp[i][j][k]={max(dp[i1][j][k],dp[i1][jx][ky]+1)dp[i1][j][k]x<=j and y<=kother
  • 返回dp[len(strs)][m][k]
class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        def count01(strc):
            x = 0
            for c in strc:
                if c == '0':x+=1
            return x,len(strc)-x
        N = len(strs)
        dp = [[[0 for k in range(n+1)] for j in range(m+1)] for i in range(N+1)]
        for i in range(1,N+1):
            x,y = count01(strs[i-1])
            for j in range(m+1):
                for k in range(n+1):
                    if j>=x and k>=y: dp[i][j][k] = max(dp[i-1][j][k],dp[i-1][j-x][k-y]+1)
                    else: dp[i][j][k] = dp[i-1][j][k]
        return dp[N][m][n] 

N表示数组长度,m,n表示数字0,1容量;

  • 时间复杂度O(Nmn+L),L表示遍历所有字符串的0,1数目情况;
  • 空间复杂度:O(Nmn)

压缩01背包

0-1背包在压缩空间的情况下是逆向遍历,完全背包(取用物品可无限多次)在压缩空间是正向遍历。

注意到每次的遍历只和i-1有关,所以只用存储二维信息即可;

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        def count01(strc):
            x = 0
            for c in strc:
                if c == '0':x+=1
            return x,len(strc)-x
        dp = [[0 for j in range(n+1)] for i in range(m+1)]
        for s in strs:
            x,y = count01(s)
            for i in range(m,x-1,-1):
                for j in range(n,y-1,-1):
                    dp[i][j] = max(dp[i][j],dp[i-x][j-y]+1)
        return dp[m][n] 

N表示数组长度,m,n表示数字0,1容量;

  • 时间复杂度O(Nmn+L),L表示遍历所有字符串的0,1数目情况;
  • 空间复杂度:O(mn)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值