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

一、题目描述

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

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

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
在这里插入图片描述
提示:

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

二、解题思路

这道题抓住题目中的几个点去思考,每次是任意取两块石头,求差值,剩下重量较大的石头的剩余部分。直到石头取完或者剩下一块石头。我可以每一回合取完石头先不求差值,放在两个堆中,当取完数组中的石头是,最后石头被分成了两堆,这时两堆石头中重量的差值。此时问题就转化成了,如何让这个差值最小的问题。

那又如何去考虑这个问题呢?

既然知道每块石头的重量,然后我们又需要将石头分成两堆,最好的情况是两堆的重量相等,差值为0,每一堆的重量是总量的一半:target=sum/2,当然没我要做的就是如何使得每一堆的重量能够最接近target就行。

使用动规五部曲:

第一步:确定dp数组以及下标的含义

dp[j]表示容量为j的背包,最多可以背dp[j]这么重的⽯头

第二步:确定递推公式

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

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

不明白这里的先看一下49_01背包问题

dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);

第三步: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]才不会初始值所覆盖。

第四步:确定遍历顺序

如果使用的是一维数组,物品遍历的for循环放在外层,遍历背包的for放在内层。内层用倒叙。

for (int i = 0; i < stones.lenght; i++) { // 遍历物品
	for (int j = target; j >= stones[i]; j--) { // 遍历背包
		dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
	}
}

第五步:举例推导dp数组

举例,输⼊:[2,4,1,1],此时target = (2 + 4 + 1 + 1)/2 = 4 ,dp数组状态图如下:
在这里插入图片描述
最后dp[target]⾥是容量为target的背包所能背的最⼤重量。

那么分成两堆⽯头,⼀堆⽯头的总重量是dp[target],另⼀堆就是sum - dp[target]。

在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target]⼀定是⼤于等于

dp[target]的。

那么相撞之后剩下的最⼩⽯头重量就是 (sum - dp[target]) - dp[target]。

三、代码演示

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int[] dp = new int[15001];

        int sum =0;

        for(int i=0; i<stones.length; i++){
            sum += stones[i];
        }

        int target = sum/2;

        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]-dp[target];

    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值