1049 最后一块石头的重量 II

本文最后更新于:2021年2月3日 下午

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

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

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

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

示例:

1
2
3
4
5
6
7
输入:[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. 1 <= stones.length <= 30
  2. 1 <= stones[i] <= 1000

Solution

参考 @carlsun-2 、方法同 [416 分割等和子集]

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

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

  • 在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的。那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
vector<int> dp(15001, 0);
int sum = 0;
for(int stone : stones)
sum += stone;
int target = sum/2;
for(int i=0; i<stones.size(); ++i){
for(int j=target; j>=stones[i]; --j)
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i]);
}
return (sum-dp[target])-dp[target];
}
};

话外:

咋一看,是不是可以先排序,然后取出最后两个石头一起粉碎,如果有残余则再插入到数组中,似乎示例就是这种方法。然而这个规律并没有道理,例如 [31,26,33,21,40] 的最小重量为 5,如果采用上述方法得到的结果为 9.


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!