这是我参与更文挑战的第9天
,活动详情查看更文挑战
有一堆石头,用整数数组 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
}
}
}