0%

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

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

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

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

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

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

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

示例:

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

动态规划

此处的题设中,对于石头选取的要求是任意选取,但是要求返回的内容是最后剩余石头的最小可能重量,本质上还是在求解问题的最优解。

重点还是在于问题的划分。首先,需要对问题进行转换,将上述问题转化为:将石头分为重量作为接近的两堆,那么最后剩余的最小石头重量就是两堆石头重点之间的差值,并且对于两堆石头的数量没有任何要求。

假设第一堆石头为: 第二堆石头为 ,那么首先 做相减,若是 消去后将剩余的元素 加入到石堆 中,那么下一次就在 中选择 的值进行消去。反之,则在 中进行选择。相等则同时消去两个。交替进行,最后两堆只会剩下一个或零个的元素。

  1. 动态规划数据的定义:首先计算总体的石头的重量 sum ,然后令 n=sum//2 ,定义动态规划数组 dp[n] 表示容量为 n 的背包能否恰好装满,初始时仅在元素 0 中选取。
    \
  2. 动态规划数组的推导:在第 i 次迭代中,从元素 0-i 中选取,向背包中添加石头。那么 dp[j]=dp[j]||dp[j-stones[i]] if j>=stones[i] else dp[j]

  3. 动态规划数组的初始化:首先全部初始化为 false ,当背包容量为0时,本身就是装满的状态: dp[0]=true ,若是 n>=stones[0] 那么 dp[stones[0]]=true

  4. 数组的便利顺序:外层循环,对石头进行遍历, i=1,...,stones.size()-1 ,内层循环对背包进行遍历, j=n,...,stones[i] ,在完成迭代后,再对 dp[n] 从后向前遍历寻找第一个为 true 的元素 dp[k] ,那么那么最终的结果为 sum-2k

  5. 举例验证: stones = [2,7,4,1,8,1]

初始的动态规划数组:[1,0,1,0,0,0,0,0,0,0,0,0]
逐次迭代的动态规划数组:
[1,0,1,0,0,0,0,1,0,1,0,0]
[1,0,1,0,1,0,1,1,0,1,0,1]
[1,1,1,1,1,1,1,1,1,1,1,1]
[1,1,1,1,1,1,1,1,1,1,1,1]
[1,1,1,1,1,1,1,1,1,1,1,1]
最终的输出为0

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
if(stones.size()==1) return stones.front();
int sum=0;
for(auto a:stones) sum+=a;
vector<bool> dp(sum/2+1,false);
dp[0]=true;
if(stones[0]<=sum/2) dp[stones[0]]=true;
for(int i=1;i<stones.size();i++){
for(int j=dp.size()-1;j>=stones[i];j--){
dp[j]=dp[j]||dp[j-stones[i]];
}
}
int i;
for(i=dp.size()-1;!dp[i];i--);
return sum-(2*i);
}
};
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道