/**
* @param {number[]} stones
* @return {number}
*/
var lastStoneWeightII = function(stones) {
// dp 数组最大容量是 max(stones[length]) * max(stones[i]) /2 + 1
// dp[j]的含义是 容量为j的背包 最多可以背dp[j]这么多的石头
let dp = new Array(15001).fill(0);
let sum = 0;
for(let i = 0;i<stones.length;i++){
sum += stones[i];
}
// target 需要向下取整 js里的parseInt去掉小数点可以满足
let target =parseInt(sum/2);
// 遍历 先正序物品 再倒序背包
for(let i = 0;i<stones.length;i++){
for(let j = target;j>=stones[i];j--){
dp[j] =Math.max(dp[j],dp[j - stones[i]] + stones[i])
}
}
return sum - dp[target] - dp[target];
};
把石头分成质量接近于 sum/2 的两堆,注意sum/2 是整数,这里我是向下取整的,因为求的是被减石头堆的重量,也就是小的那堆。
dp数组含义:容量为 j 的背包能装的石头重量为dp[j] ,所以按照题意,dp的长度为石头重量的一半。初始化,因为石头重量肯定是大于零的,为了之后初始化值都可以被覆盖,所以初始化为零。
然后就是传统的01背包遍历了,先遍历物品,再遍历背包(倒序遍历),取最大值
最后一块石头的重量:从一堆石头中,每次拿两块重量分别为x,y的石头,若x=y,则两块石头均粉碎;若x<y,两块石头变为一块重量为y-x的石头求最后剩下石头的最小重量(若没有剩下返回0)
问题转化为:把一堆石头分成两堆,求两堆石头重量差最小值
进一步分析:要让差值小,两堆石头的重量都要接近sum/2;我们假设两堆分别为A,B,A<sum/2,B>sum/2,若A更接近sum/2,B也相应更接近sum/2
进一步转化:将一堆stone放进最大容量为sum/2的背包,求放进去的石头的最大重量MaxWeight,最终答案即为sum-2*MaxWeight