动态规划 10. 最后一块石头的重量II
1049. 最后一块石头的重量 II - 力扣(LeetCode)
难度 7 - 中等
依然没有独立做出来,看题解之后才知道——原来如此!
方法一
-
思路:
代码随想录:
本题其实是尽量让石头分成重量相同的两堆(尽可能相同),相撞之后剩下的石头就是最小的。
一堆的石头重量是sum,那么我们就尽可能拼成 重量为 sum / 2 的石头堆。 这样剩下的石头堆也是 尽可能接近 sum/2 的重量。 那么此时问题就是有一堆石头,每个石头都有自己的重量,是否可以 装满 最大重量为 sum / 2的背包。
看到这里,大家是否感觉和昨天讲解的 416. 分割等和子集 (opens new window)非常像了,简直就是同一道题。
本题这样就化解成01背包问题了。
416. 分割等和子集 (opens new window)是求背包是否正好装满,而本题是求背包最多能装多少。
物品就是石头,物品的重量为stones[i],物品的价值也为stones[i]。
原来就是要想到,要把石头尽量分成重量相同的两堆!!!
想到这个基本上就能想到用01背包来解决了
具体的01背包解决方法可以看动态规划 8. 01背包问题解法(二维dp数组、滚动数组)以及python中输入的处理方法-CSDN博客和动态规划 9. 分割等和子集-CSDN博客
这道题转化为01背包之后与动态规划 9. 分割等和子集-CSDN博客就非常相似了
-
代码:
class Solution: def lastStoneWeightII(self, stones: List[int]) -> int: total = sum(stones) target = total // 2 # 定义dp[j]: 背包容量为j时,装下的最大的石头总重量 dp = [0] * (target + 1) for i in range(len(stones)): for j in range(target, stones[i] - 1, -1): dp[j] = max(dp[j], dp[j-stones[i]] + stones[i]) return total - 2 * dp[-1]
-
时间复杂度:O(m × n) , m是石头总重量(准确的说是总重量的一半),n为石头块数
-
空间复杂度:O(m)
方法二:
-
直接摘录代码并详注如下:
class Solution: def lastStoneWeightII(self, stones: List[int]) -> int: total_sum = sum(stones) # 计算所有石头的总重量 target = total_sum // 2 # 目标是找到一个子集,使其总和尽可能接近 total_sum / 2 # 创建二维 DP 数组,大小为 (len(stones) + 1) x (target + 1) # dp[i][j] 表示前 i 个石头能否凑出重量 j dp = [[False] * (target + 1) for _ in range(len(stones) + 1)] # 初始化:前 i 个石头组成总重量 0 总是可行的(空集的情况) for i in range(len(stones) + 1): dp[i][0] = True # 遍历所有石头 for i in range(1, len(stones) + 1): # 遍历每块石头 for j in range(target, -1, -1): # 遍历背包重量 j,从大到小遍历 if stones[i - 1] <= j: # 选择是否加入当前石头,取决于是否能组成重量 j dp[i][j] = dp[i - 1][j] or dp[i - 1][j - stones[i - 1]] else: # 当前石头超过了 j 的容量,不能选 dp[i][j] = dp[i - 1][j] # **寻找最接近 target 的可行子集和 j** # 逆序遍历 dp[len(stones)][j],找到最大可能的 j for j in range(target, -1, -1): # 从 target 开始递减 # dp[len(stones)][i] == True 表示在所有石头中,可以找到一个子集,使得子集和等于 i; # dp[len(stones)][target] == True 代表有一个子集,正好等于 target,这是最优解; # 如果 dp[len(stones)][target] == False, # 则需要找最大的i,使得dp[len(stones)][i] == True,确保背包实际重量尽可能接近 target。 if dp[len(stones)][j]: # 从右往左找到第一个 dp[len(stones)][j] 为 True 的 j # 计算最终剩余的最小重量差 return total_sum - 2 * j # total_sum - 2 * j 代表两堆重量的差值 return 0 # 理论上不会发生