代码随想录第42天| 416. 分割等和子集

129 阅读2分钟

416. 分割等和子集

1. first idea

这实际上是一个典型的01背包,

一开始我觉得情况有些复杂,两个子数组要互相协调,比较难控制。

但是我在拖地的时候突然想到这实际上是一个固定量:half=nums2half=\frac{\sum nums}{2}

我们可以把这个问题看作一个01背包的拓展:

  1. 背包的初始剩余容量是halfhalf;
  2. 每个元素是一个物品,每个物品的重量是numnum,价格和重量相等,因为我们需要追求在half限制条件下尽可能装满背包;
  3. 我们的目标是从nums中选取物品,直到装满half,或者直到没有足够的剩余容量装任何剩余物品。

2. doc reading

代码随想录 (programmercarl.com) 所以基于这一思路,我们开始实现。

dp[i][j]表示前0~i号物品,背包当前临时容量为j的情况下,背包最大价值总和。(当前临时容量是我们假设的子情况,也就是假设背包的容量不是真实的总容量,而是j。)

递推式:

dp[i][j]=max(dp[i1][j],dp[i1][jweights[i]]+value[i])dp[i][j] = \max(dp[i - 1][j], dp[i - 1][j - weights[i]] + value[i])

其中dp[i - 1][j - weights[i]]表示刨去当前选择要装入背包的第i号物品重量后,剩下的临时容量j-weights[i]]在前0~i-1号物品挑选后的最大价值。

初始化: 正如01背包初始化,我们对dp的左边列,也就是当前临时容量为0的列,赋值0,对第一行只遍历0号物品的总价值赋值。

最后看右下角遍历完所有物品后的,且背包当前临时容量为真实容量half的情况下,总的价值。

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        values = weights = nums
        if (sum(nums) % 2) == 1:
            return False
        half = sum(nums) // 2
        dp = [[0] * (half + 1) for _ in range(len(nums))]
        for bag_tmp_contain in range(weights[0], half + 1):
            dp[0][bag_tmp_contain] = values[0]
        for ob_idx in range(1, len(nums)):
            # 遍历每一个物品
            for bag_tmp_contain in range(0, half + 1):
                # 遍历背包临时容量
                if bag_tmp_contain < weights[ob_idx]:
                    dp[ob_idx][bag_tmp_contain] = dp[ob_idx - 1][bag_tmp_contain]
                else:
                    dp[ob_idx][bag_tmp_contain] = max(
                        dp[ob_idx - 1][bag_tmp_contain], 
                        dp[ob_idx - 1][bag_tmp_contain - weights[ob_idx]] + values[ob_idx]
                    )
        return dp[-1][-1] == half