返回

巧用动规算法,揭秘 LeetCode 474:最大一和零子集

前端

前言

算法的世界里,LeetCode 是一座无法绕过的丰碑。它以其丰富且具有挑战性的题目库,吸引着无数程序员前来探索与学习。LeetCode 474:最大一和零子集,作为一道中等难度的题目,旨在考察算法爱好者对动态规划的掌握程度。

问题

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。请你找出并返回 strs 的最大子集的长度,该子集中最多包含 m 个 0 和 n 个 1 。

示例

  • 输入:strs = ["10", "0001", "111001", "1", "0"], m = 1, n = 2

    • 输出:4
  • 输入:strs = ["10", "0", "1"], m = 1, n = 1

    • 输出:2
  • 输入:strs = ["0", "0", "0"], m = 2, n = 1

    • 输出:2

解决方案

要解决这个问题,我们首先需要明确问题的本质:给定一个二进制字符串数组 strs,以及两个整数 m 和 n,要求我们找出 strs 的一个子集,使得该子集中 0 的数量不超过 m,1 的数量不超过 n,同时该子集的长度尽可能大。

这个问题本质上是一个背包问题。我们可以将问题简化为:给定一个物品重量数组 weight 和一个物品价值数组 value,以及两个整数 bagWeight 和 bagValue,要求我们找出这些物品的一个子集,使得这些物品的总重量不超过 bagWeight,这些物品的总价值不超过 bagValue,同时这些物品的总价值尽可能大。

这个问题的动态规划解法如下:

  1. 定义状态:dp[i][j][k] 表示前 i 个物品中,选择了 j 个 0 和 k 个 1 的最大价值。
  2. 状态转移方程:
    • dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-1][k] + value[i], dp[i-1][j][k-1] + value[i])
    • 其中,dp[i-1][j][k] 表示前 i-1 个物品中,选择了 j 个 0 和 k 个 1 的最大价值,dp[i-1][j-1][k] + value[i] 表示前 i-1 个物品中,选择了 j-1 个 0 和 k 个 1 的最大价值,再加上第 i 个物品的价值,dp[i-1][j][k-1] + value[i] 表示前 i-1 个物品中,选择了 j 个 0 和 k-1 个 1 的最大价值,再加上第 i 个物品的价值。
  3. 边界条件:
    • dp[0][0][0] = 0
  4. 最终结果:
    • dp[n][m][n]

代码实现

def findMaxSubset(strs, m, n):
    # 初始化动态规划数组
    dp = [[[0 for _ in range(n+1)] for _ in range(m+1)] for _ in range(len(strs)+1)]

    # 计算动态规划数组
    for i in range(1, len(strs)+1):
        for j in range(0, m+1):
            for k in range(0, n+1):
                dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-1][k] + (strs[i-1] == '0'), dp[i-1][j][k-1] + (strs[i-1] == '1'))

    # 返回最终结果
    return dp[len(strs)][m][n]

# 测试代码
strs = ["10", "0001", "111001", "1", "0"]
m = 1
n = 2
print(findMaxSubset(strs, m, n))

总结

通过本文对 LeetCode 474:最大一和零子集的详细分析,我们不仅掌握了这一算法题的解法,更重要的是,我们学习了动态规划思想在解决此类问题中的应用。相信通过不断的练习和探索,您一定能够成为算法世界里的王者。