495 字
2 分钟
代码随想录算法训练营第三十一天 | 动态规划 Part04
1049. 最后一块石头的重量 II
LeetCode 1049. Last Stone Weight II
- 本质:将所有石头分为两个子集,目标是找到最接近总重量一半的子集重量。
- 转换为0-1 背包问题:每个石头只能选或不选。
class Solution: def lastStoneWeightII(self, stones: List[int]) -> int: sum_weight = sum(stones) # dp[i][j] 表示前 i 个石头能否组成重量 j 的最大重量 dp = [[0] * (sum_weight // 2 + 1) for _ in range(len(stones) + 1)]
for i in range(1, len(stones) + 1): cur = stones[i - 1] for j in range(sum_weight // 2 + 1): if j >= cur: # 可以选当前石头 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur] + cur) else: # 不能选当前石头 dp[i][j] = dp[i - 1][j]
# 最大可达的重量 max_weight,差值即为 total - 2 * max_weight max_weight = dp[len(stones)][sum_weight // 2] return sum_weight - max_weight * 2
494. 目标和
- 转化为回溯 + 记忆化搜索问题。
- 每个位置尝试 + 或 -,使用备忘录避免重复计算。
class Solution: def findTargetSumWays(self, nums: List[int], target: int) -> int: self.memo = {} if len(nums) == 0: return 0 return self.dp(nums, 0, target)
def dp(self, nums, i, remain): # 终止条件:遍历完整个数组 if i == len(nums): return 1 if remain == 0 else 0
key = f"{i},{remain}" if key in self.memo: return self.memo[key]
# 选择:加 nums[i] 或 减 nums[i] result = self.dp(nums, i + 1, remain - nums[i]) + \ self.dp(nums, i + 1, remain + nums[i])
self.memo[key] = result return result
474. 一和零
- 这是一个三维 0-1 背包问题。
- 状态定义:
dp[i][j][k]
表示前 i 个字符串,使用 j 个 0 和 k 个 1 的最大子集大小。
class Solution: def findMaxForm(self, strs: List[str], m: int, n: int) -> int: # 初始化三维数组:dp[i][j][k] dp = [[[0] * (n + 1) for _ in range(m + 1)] for _ in range(len(strs) + 1)]
for i in range(1, len(strs) + 1): curStr = strs[i - 1] zeroCount = curStr.count('0') oneCount = curStr.count('1')
for j in range(m + 1): for k in range(n + 1): if j >= zeroCount and k >= oneCount: # 两种情况:选 or 不选 dp[i][j][k] = max( dp[i - 1][j - zeroCount][k - oneCount] + 1, dp[i - 1][j][k] ) else: # 容量不足,不能选 dp[i][j][k] = dp[i - 1][j][k]
return dp[len(strs)][m][n]
代码随想录算法训练营第三十一天 | 动态规划 Part04
https://fuwari.vercel.app/posts/code_musings_day_thirty-one/