leetcode 474. 一和零(01背包问题)
leetcode 2021/06/06 每日一题 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
解题思路
- 数组长度c,m个0,n个1
- 使用一个cmn的三维数组记录dp[k][i][j]
- 初始化:dp = [[[0]*(n+1) for _ in range(m+1)] for _ in range(c+1)]
- 边界:k=0 或 (i=0 或j=0) 时,dp[k][i][j] = 0
- 外层循环:k个元素,从1到c循环
- 中层循环:i个0,从1到m循环
- 内层循环:j个1,从1到n循环
- 第k个元素为s,0的个数为 s0 = s.count('0'),1的个数为 s1 = s.count('1')
- 当s0 > i or s1 > j:dp[k][i][j] = dp[k-1][i][j]
- else:dp[k][i][j] = max(dp[k-1][i][j], dp[k-1][i-s0][j-s1]+1)
python代码
def findMaxForm(strs, m, n):
c = len(strs)
dp = [[[0]*(n+1) for _ in range(m+1)] for _ in range(c+1)]
for k in range(1, c+1):
s0 = strs[k-1].count('0')
s1 = strs[k-1].count('1')
for i in range(1, m+1):
for j in range(1, n+1):
if i < s0 or j < s1:
dp[k][i][j] = dp[k - 1][i][j]
else:
dp[k][i][j] = max(dp[k - 1][i][j], dp[k - 1][i - s0][j - s1] + 1)
return dp[c][m][n]
print(findMaxForm(["10", "0001", "111001", "1", "0"], 5, 3))
print(findMaxForm(["10", "0", "1"], 1, 1))
优化思路
使用二维数组,只需存储上一层dp[i-s0][j-s1],逆序更新
参考动态规划之01背包问题
优化python代码
def findMaxForm(strs, m, n):
dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
for s in strs:
s0 = s.count('0')
s1 = s.count('1')
for i in range(m, s0-1, -1):
for j in range(n, s1-1, -1):
dp[i][j] = max(dp[i][j], dp[i-s0][j-s1]+1)
return dp[m][n]
print(findMaxForm(["10", "0001", "111001", "1", "0"], 5, 3))
print(findMaxForm(["10", "0", "1"], 1, 1))
运行结果: