目 录CONTENT

文章目录

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

小王同学
2024-01-01 / 0 评论 / 0 点赞 / 85 阅读 / 0 字

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

力扣传送门(opens new window)

题目难度:中等

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;

如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。

最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。

示例:

  • 输入:[2,7,4,1,8,1]
  • 输出:1

解释:

  • 组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
  • 组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
  • 组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
  • 组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 1000

解题思路:

分析:

我们由题目可以知道经过n次粉碎后,最终最多只会剩下1个石头,并且我们需要让最后一块石头的质量最小
我们继续分析可以发现(关键):我们可以将这一堆石头分成两堆(heap1和heap2)
我们不妨设 heap1总质量 >= heap2总质量,而最后的结果就是heap1 - heap2,我们只需要保证heap1 - heap2最小即可

如何计算:

我们可以先求出这一堆石头的总质量sum,
而sum = heap1 + heap2 (heap1 > heap2)
heap1 - heap2 = sum - 2 * heap2
要求heap1 - heap2 的最小值,就可以转化成求sum - 2 * heap2 的最小值,
也就转化成了求 2 * heap2 的最大值,也就是求heap2的最大值(前提:sum - 2 * heap2 >= 0 等价于 heap2 <= sum / 2)
那么就转化成了01背包问题:背包的最大容量为 sum / 2

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

是不是感觉和 416. 分割等和子集 非常像了。

本题物品的重量为stones[i],物品的价值也为stones[i]。

对应着01背包里的物品重量weight[i]和 物品价值value[i]。

  1. 确定dp数组以及下标的含义

dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。

可以回忆一下01背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j]。

相对于 01背包,本题中,石头的重量是 stones[i],石头的价值也是 stones[i] ,可以 “最多可以装的价值为 dp[j]” == “最多可以背的重量为dp[j]”

  1. 确定递推公式

01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

一些同学可能看到这dp[j - stones[i]] + stones[i]中 又有- stones[i] 又有+stones[i],看着有点晕乎。

大家可以再去看 dp[j]的含义。

  1. dp数组如何初始化

既然 dp[j]中的j表示容量,那么最大容量(重量)是多少呢,就是所有石头的重量和。

因为提示中给出1 <= stones.length <= 30,1 <= stones[i] <= 1000,所以最大重量就是30 * 1000 。

而我们要求的target其实只是最大重量的一半,所以dp数组开到15000大小就可以了。

当然也可以把石头遍历一遍,计算出石头总重量 然后除2,得到dp数组的大小。

我这里就直接用15000了。

接下来就是如何初始化dp[j]呢,因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式 dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。

  1. 确定遍历顺序

物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!

代码实现:

/**
 * 1049. Last Stone Weight II
 * 最后一块石头的重量 II
 */
public class LastStoneWeightII {
    public int lastStoneWeightII(int[] stones) {
        if(stones == null || stones.length == 0){
            return 0;
        }
        int sum = 0;
        for(int item : stones){
            sum+= item;
        }
        int target = sum >> 1;
        int[] dp = new int[target+1];
        //遍历石头
        for(int i = 0;i < stones.length;i++){
            for(int j = target;j >= stones[i];j--){
                dp[j] = Math.max(dp[j],dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - dp[target] * 2;
    }
}

代码跟踪:

让我们来计算一个简单的例子 [2, 4, 1, 1]。

对于给定的石头重量列表 [2, 4, 1, 1],我们将按照之前的方法计算 dp 数组。

首先,我们需要计算石头的总重量。在这个例子中,总重量为 2 + 4 + 1 + 1 = 8。

此时target = (2 + 4 + 1 + 1)/2 = 4

然后,我们需要创建一个长度为 (总重量 + 1) / 2 = (8 + 1) / 2 = 4.5,但由于数组长度必须为整数,我们将其取整为 4 的数组 dp,并将其所有元素初始化为 0。

现在,我们开始计算 dp 数组。

对于第一个石头 2:

  • 内层循环从 target = 4 递减到 2:
    • j = 4,dp[4] = max(dp[4], dp[4 - 2] + 2) = max(0, dp[2] + 2) = 2
    • j = 3,dp[3] = max(dp[3], dp[3 - 2] + 2) = max(0, dp[1] + 2) = 2
    • j = 2,dp[2] = max(dp[2], dp[2 - 2] + 2) = max(0, dp[0] + 2) = 2

对于第二个石头 4:

  • 内层循环从 target = 4 递减到 4:
    • j = 4,dp[4] = max(dp[4], dp[4 - 4] + 4) = max(2, dp[0] + 4) = 4

对于第三个石头 1:

  • 内层循环从 target = 4 递减到 1:
    • j = 4,dp[4] = max(dp[4], dp[4 - 1] + 1) = max(4, dp[3] + 1) = 4
    • j = 3,dp[3] = max(dp[3], dp[3 - 1] + 1) = max(2, dp[2] + 1) = 3
    • j = 2,dp[2] = max(dp[2], dp[2 - 1] + 1) = max(2, dp[1] + 1) = 2
    • j = 1,dp[1] = max(dp[1], dp[1 - 1] + 1) = max(0, dp[0] + 1) = 1

对于第四个石头 1:

  • 内层循环从 target = 4 递减到 1:
    • j = 4,dp[4] = max(dp[4], dp[4 - 1] + 1) = max(4, dp[3] + 1) = 4
    • j = 3,dp[3] = max(dp[3], dp[3 - 1] + 1) = max(3, dp[2] + 1) = 3
    • j = 2,dp[2] = max(dp[2], dp[2 - 1] + 1) = max(2, dp[1] + 1) = 2
    • j = 1,dp[1] = max(dp[1], dp[1 - 1] + 1) = max(1, dp[0] + 1) = 1

完成计算后,dp 数组为 [0,1,2,3,4]。

根据题目要求,我们需要找到总重量之差最小的两堆石头,因此我们要从 dp 数组的后半部分开始寻找最接近 target 的值。在这个例子中,dp[4] = 4 是最接近 target = 4 的值。

因此,总重量之差最小的两堆石头的重量之差为 4 - 4 = 0。

  • 时间复杂度:O(m × n) , m是石头总重量(准确的说是总重量的一半),n为石头块数
  • 空间复杂度:O(m)
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区