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

202 阅读2分钟

这是我参与更文挑战的第9天 ,活动详情查看更文挑战 image.png 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

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

如果 x == y,那么两块石头都会被完全粉碎; 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。 最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。

 

示例 1:

输入:stones = [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],这就是最优值。

解题思路

两个石头a,b碰撞,产生一个新石头a-b(a>=b),可以看成是两个石头相加,重量分别是a和-b,因此我们可以令若干个石头的权重变为负数,这样就将石头分为了正负两堆,每次碰撞都从它们里面各取一块石头,进行碰撞,根据结果的正负号重新把新产生的石头分到这两堆上,这样两堆的差值就会保持不变,并且差值dif就是最终结果

  • 石头总和sum
  • 负数堆权重总和neg
  • 正数堆权重总和sum-neg (sum-neg)-neg=dif dif=sum-2*neg 要使得dif尽量小,那就要使得neg尽量大,并且不能超过sum/2

所以这题就要要选出若干个数,作为负数,使得这些负数的总和尽量接近sum/2

数组含义

dp[i][j]表示前i个石头是否存在neg=j的情况,

状态转移

  • j<stones[i] 当前需要的neg总和太小,不能取当前石头 dp[i+1][j]=dp[i][j]
  • j>=stones[i]

当前neg总和够大了,可以选择取或者不取,只要能拼凑成j就可以了 dp[i+1][j]=dp[i][j]||dp[i][j-stones[i]]

代码

func lastStoneWeightII(stones []int) int {

	sum:=0
	for _, stone := range stones {
		sum+=stone
	}
	n:=sum/2
	dp := make([][]bool, len(stones)+1)
	for i := 0; i <= len(stones); i++ {
		dp[i] = make([]bool, n+1)
	}
	dp[0][0]=true
	for i := 0; i < len(stones); i++ {

		for j := 0; j <= n; j++ {

			if j<stones[i]{
               dp[i+1][j]=dp[i][j]
			}else {
				dp[i+1][j]=dp[i][j]||dp[i][j-stones[i]]
			}

		}
	}
	for i := n;; i-- {
		if dp[len(stones)][i]{
			return sum-2*i
		}
	}

}