动态规划 10. 最后一块石头的重量II

动态规划 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  # 理论上不会发生
    
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值