代码随想录算法训练营第四十三天 | 动态规划 part 5 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零

目录

  • 1049. 最后一块石头的重量 II
    • 思路
    • 代码
  • 494. 目标和
    • 思路
    • 代码
  • 474.一和零
    • 思路
    • 代码

1049. 最后一块石头的重量 II

Leetcode

代码随想录算法训练营第四十三天 | 动态规划 part 5 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第1张图片

思路

本题尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。

所以本题和416. 分割等和子集几乎一样。

不同的地方在于,想要得到最后剩下的石头的重量,return sum(stones) - dp[target] * 2

代码

dp数组是True 或者False. dp[j]为j重量下能否正好由一个或多个石头组成

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        n = len(stones)
        total = sum(stones)
        target = total // 2

        dp = [False] * (target + 1)
        dp[0] = True

        for stone in stones:
            for j in range(target, stone - 1, -1):
                dp[j] = dp[j] or dp[j-stone]
        
        for j in range(target, -1, -1):
            if dp[j]:
                return total - 2 * j

简化
dp[j]表示为j重量下能放入重量(价值)最大石头。对于单独的石头,重量和价值相等。

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        total = sum(stones)
        target = total // 2
        dp = [0] * (target + 1)
        for stone in stones:
            for j in range(target, stone - 1, -1):
                dp[j] = max(dp[j], dp[j - stone] + stone)
        return total - 2*dp[target]
  • 时间复杂度:O(m × n) , m是石头总重量(准确的说是总重量的一半),n为石头块数
  • 空间复杂度:O(m)

494. 目标和

Leetcode
代码随想录算法训练营第四十三天 | 动态规划 part 5 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第2张图片

思路

本质上这道题可以将nums内的非负整数分成两组,一组left为正整数,一组right为将要被减去的数字。

我们做出如下推导:

  1. left + right = sum(nums)
  2. left - right = target => right = left - target
  3. 将2带入1:left + (left - target) = sum(nums)
    => 2*left = sum(nums) + target
    => left = sum(nums) + target // 2

这道题被分解成为了,已知sum(nums)target,求nums中的数字有几种能组合成left的方式。所以,通过left = sum(nums) + target // 2这个等式,我们将问题化简成01背包问题。

但是此题和前面的题目有一些区别,01背包求的是背包装入的最大价值,分割等和子集求的是能否装满背包,最后一块石头的重量求的是能装的最大重量(等同于能装的最大价值),此题求的是给定背包容量,有多少种把背包装满的方式。

二维dp

下图的neg可以视作我们定义的left或者right,不影响。
代码随想录算法训练营第四十三天 | 动态规划 part 5 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第3张图片

一维dp

可以看成是将二维dp压缩之后的结果

代码

二维dp

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        total_sum = sum(nums)  # 计算nums的总和
        # 注意这里需要使用target的绝对值
        if abs(target) > total_sum:
            return 0  # 此时没有方案
        if (target + total_sum) % 2 == 1:
            return 0  # 此时没有方案
        target_sum = (target + total_sum) // 2  # 目标和

        # 创建二维动态规划数组,行表示选取的元素数量,列表示累加和
        dp = [[0] * (target_sum + 1) for _ in range(len(nums) + 1)]

        # 初始化状态, 0个元素中不选取的方法有一种
        dp[0][0] = 1

        # 动态规划过程
        for i in range(1, len(nums) + 1):
            for j in range(target_sum + 1):
                dp[i][j] = dp[i - 1][j]  # 不选取当前元素
                if j >= nums[i - 1]:
                    dp[i][j] += dp[i - 1][j - nums[i - 1]]  # 选取当前元素

        return dp[len(nums)][target_sum]  # 返回达到目标和的方案数

一维dp

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        total = sum(nums)
        if abs(target) > total:
            return 0
        if (total + target) % 2 != 0:
            return 0
        left = (total + target) // 2

        dp = [0] * (left + 1)
        dp[0] = 1

        for num in nums:
            for j in range(left, num - 1, -1):
                dp[j] += dp[j - num]
        
        return dp[-1]

474.一和零

Leetcode

代码随想录算法训练营第四十三天 | 动态规划 part 5 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第4张图片

思路

本题m,n相当于两个维度的01背包。

找最大子集相当于找 让这个2维的背包装满 最多能放多少个物品。

回想一下01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

这道题的递推公式为: dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1)

本题相当于多维dp数组压缩成2维了,所以遍历背包的时候需要倒序。

代码

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
    	# n, m 顺序可以对调,无所谓,因为是属于遍历背包的过程
        dp = [[0] * (m + 1) for _ in range(n + 1)]

        for s in strs:
            zeros = s.count("0")
            ones = s.count("1")

            for i in range(n, ones - 1, -1):
                for j in range(m, zeros - 1, -1):
                    dp[i][j] = max(dp[i][j], dp[i - ones][j - zeros] + 1)
                    
        return dp[n][m]
  • 时间复杂度: O(kmn),k strs的长度
  • 空间复杂度: O(mn)

你可能感兴趣的:(代码随想录算法训练营,算法,动态规划,leetcode,python)