「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。
1049. 最后一块石头的重量 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],这就是最优值。
示例 2:
输入: stones = [31,26,33,21,40]
输出: 5
示例 3:
输入: stones = [1,2]
输出: 1
解析
/**
* @brief 背包问题
* 本题可以看作是上一题的变体,将石头分成两堆,然后两堆相互抵消,剩下最小的石头
* 物品的重量为 stones[i],物品的价值也为stones[i]
* 对应01背包里的物品重量weight[i]和物品价值value[i]
*
* 动规五部曲(四部曲)
* 1、确定dp数组以及下标的含义
* dp[j]表示容量为j的背包,最多可以装下dp[j]重量的石头
* 2、确定地推公式
* 01背包的递推公式为:dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
* 本题:
* dp[j] = max(dp[j],dp[j-stones[i]]+stones[i]);
* 3、dp数组的初始化
* 提示中给出: 1<=stones.length <=30 , 1<=stones[i]<=1000.
* 所以最大重量为 30*1000 = 30000
* 所以这里dp数组大小为 15000
*
* 石头都是有重量的,所以可以将dp[j]全部初始化为0
*
* vector<int> dp(15000,0);
* 4、确定遍历顺序
* 使用一维dp数组时,物品遍历的for循环放在最外层,遍历背包的for循环放在内层,且内层for循环使用倒序
*
*
*/
代码
class Solution
{
public:
int lastStoneWeightII(vector<int> &stones)
{
int size = stones.size();
int sum = 0;
for (auto v : stones)
{
sum += v;
}
// 将物品分成两部分
int target = sum / 2;
vector<int> dp(sum, 0);
for (int i = 0; i < size; i++) // 遍历物品
{
for (int j = target; j >= stones[i]; j--) // 遍历背包
{
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
// 因为 target 是向下取整,所以 sum - dp[target]要大于等于 dp[target]
return sum - dp[target] - dp[target];
}
};