题型描述
在计算机界中,我们总是追求用有限的资源获取最大的收益。
现在,假设你分别支配着 m 个
0
和 n 个1
。另外,还有一个仅包含0
和1
字符串的数组。你的任务是使用给定的 m 个
0
和 n 个1
,找到能拼出存在于数组中的字符串的最大数量。每个0
和1
至多被使用一次。注意:
- 给定
0
和1
的数量都不会超过100
。- 给定字符串数组的长度不会超过
600
。
示例
示例 1:
输入: Array = {“10”, “0001”, “111001”, “1”, “0”}, m = 5, n = 3
输出: 4解释: 总共 4 个字符串可以通过 5 个 0 和 3 个 1 拼出,即 “10”,“0001”,“1”,“0” 。
示例 2:
输入: Array = {“10”, “0”, “1”}, m = 1, n = 1
输出: 2解释: 你可以拼出 “10”,但之后就没有剩余数字了。更好的选择是拼出 “0” 和 “1” 。
思路:
这是一道典型的0/1背包问题,如果不知道什么0/1背包问题,我们一起学习下!
什么是0/1背包?
问题描述:给你一些商品有重量weights:[2,3,4,5]
,和对应的价值values:[3,4,5,7]
,再给你一个只能装9
背包,你该如何使你装东西更值钱?一句话说明白:就是这个商品你取与不取!
如何解决?
一般采用是动态规划
weights | values | index\bag_weight | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 3 | 1 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
3 | 4 | 2 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 | 7 |
4 | 5 | 3 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 | 12 |
5 | 7 | 4 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 10 | 11 | 12 |
理解思路:
- 当一件商品不取的时候,背包不管能装多少重量,价值都为
0
,如图表第一行所示. - 当取第一物品时候,重量为
2
,价值为3
.- 背包可以装
0
重量时候,可以不行,所以为价值为0
- 装
1
也不行,价值为0
(因为根本装不下) - 当能装
2
重量时候,这时候我们就要比较了,就是装与不装,取最大值.简单来说,dp[i][j] = max(dp[i-1][j],dp[i-1][j-weights[i-1]]+values[i-1])
这步要认真理解,很重要!最关键的
- 背包可以装
- 下面以此类推,按照上面的动态方程操作
- …
上面写代码时候需要用二维数组,但是我们可以倒着推,只需要一维数组了:
附上代码:
class Solution:
# 用二维数组
def knapSack(self, weights, values, bag_weight):
nums = len(weights)
dp = [[0] * (bag_weight + 1) for _ in range(nums + 1)]
# print(dp)
for i in range(1, nums + 1):
for j in range(bag_weight+1):
if j - weights[i - 1] >= 0:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
else:
dp[i][j] = dp[i - 1][j]
# print(dp)
return dp[-1][-1]
# 用一维数组
def knapSack1(self, weights, values, bag_weight):
nums = len(weights)
dp = [0]*(bag_weight+1)
for i in range(nums):
for j in range(bag_weight,-1,-1):
if j >= weights[i]:
dp[j] = max(dp[j],dp[j-weights[i]]+values[i])
return dp[-1]
测试代码:
a = Solution()
print(a.knapSack([3, 4, 5], [4, 5, 6], 10))
print(a.knapSack1([3, 4, 5], [4, 5, 6], 10))
结果:
11
11
完全正确!
所以,理解如何解决0/1背包问题,这道题就非常简单了
此题解决代码:
class Solution(object):
def findMaxForm(self, strs, m, n):
"""
:type strs: List[str]
:type m: int
:type n: int
:rtype: int
"""
if not strs:
return 0
nums = len(strs)
dp = [[[0] * (n + 1) for _ in range(m + 1)] for _ in range(nums + 1)]
# print(dp)
for i in range(1, nums + 1):
temp_n = len(strs[i - 1])
zero_nums = strs[i - 1].count("0")
one_nums = temp_n - zero_nums
# print(zero_nums,one_nums)
for j in range(m+1):
for k in range(n+1):
# print(i,k)
# print(zero_nums,one_nums)
if j >= zero_nums and k >= one_nums:
dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - zero_nums][k - one_nums] + 1)
else:
dp[i][j][k] = dp[i - 1][j][k]
# print(dp)
return dp[-1][-1][-1]
def findMaxForm1(self, strs, m, n):
if not strs:
return 0
nums = len(strs)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(nums):
# 先计算有多少0和1
zero_nums = 0
one_nums = 0
for alp in strs[i]:
if alp == "0":
zero_nums += 1
else:
one_nums += 1
for j in range(m,-1,-1):
for k in range(n,-1,-1):
if j >= zero_nums and k >= one_nums:
dp[j][k] = max(dp[j][k],dp[j-zero_nums][k-one_nums]+1)
# print(dp)
return dp[-1][-1]
测试代码:
a = Solution()
print(a.findMaxForm(["10", "0001", "111001", "1", "0"], m=5, n=3))
print(a.findMaxForm1(["10", "0001", "111001", "1", "0"], m=5, n=3))
显示结果:
4
4
AC!舒服!