最后一块石头的重量 II
有一堆石头,用整数数组 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],这就是最优值。
题解
最后一块石头的重量可以表示为:
∑
i
=
0
n
−
1
k
i
×
s
t
o
n
e
[
i
]
,
k
i
∈
{
−
1
,
1
}
\sum_{i=0}^{n-1}k_i\times stone[i],k_i\in \{-1,1\}
i=0∑n−1ki×stone[i],ki∈{−1,1}
意思就是按照最优策略将所有石头的重量前加上加号或者减号,然后将重量累加
假设前面为 负号 的石头的所有重量之和为 neg
则最后一块石头的重量为
s
=
(
s
u
m
−
n
e
g
)
−
n
e
g
=
s
u
m
−
2
∗
n
e
g
s = (sum-neg)-neg=sum-2*neg
s=(sum−neg)−neg=sum−2∗neg 因此只要在neg不超过 sum/2 的重量下尽可能的大一些,那么最后一块石头的重量就会小一些。
综上,问题转为体积为 sum/2 的背包,有 n 个石头,每个石头的体积和价值都是 stone[i] 的 0-1 背包问题(哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大)。
class Solution {
public:
//f[j] = f[j-stones[i]] + stones[i]
int f[1510];
int lastStoneWeightII(vector<int>& stones) {
int sum = 0;
for(auto s : stones)
sum += s;
int s = sum/2;
int n = stones.size();
for(int i=1; i<=n; ++i){
for(int j=s; j>=stones[i-1]; j--){
f[j] = max(f[j], f[j-stones[i-1]]+stones[i-1]);
}
}
return sum-2*f[s];
}
};