题目
中等
相关标签
有一堆石头,用整数数组 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
提示:
1 <= stones.length <= 30
1 <= stones[i] <= 100
思路和解题方法
使用了 0-1 背包问题的思想,通过动态规划的方式求解。
具体思路如下:
- 首先,计算所有石头的总重量
sum
。- 然后,将问题转化为将石头分成两堆,使得两堆的重量尽可能接近
sum/2
。- 创建一个大小为
15001
的动态规划数组dp
,用于记录容量为j
的背包所能装载的最大重量。- 使用双重循环遍历石头数组
stones
和背包容量j
,进行动态规划的状态转移。- 在每次状态转移时,比较当前背包容量
j
能够装载的最大重量dp[j]
和将当前石头放入背包后所能得到的重量dp[j - stones[i]] + stones[i]
,取较大值更新dp[j]
。- 最后,返回两堆石头的重量差值,即
sum - dp[target] - dp[target]
。
复杂度
时间复杂度:
O(n*m)
时间复杂度为 O(n * m)。
其中 n 是石头的数量,m 是石头总重量的一半。这是因为代码中使用了两层循环,外层循环遍历石头数组,内层循环遍历背包容量。对于每个背包容量,都需要进行一次状态转移操作,因此总共需要进行 n * m 次状态转移。
空间复杂度
O(m)
空间复杂度为 O(m)。
其中 m 是石头总重量的一半。这是因为代码中创建了一个大小为 15001 的动态规划数组
dp
,用于记录不同背包容量下的最大重量。由于背包容量的最大值为石头总重量的一半,因此数组dp
的大小为 m+1,即 15001。因此,所需的额外空间随着石头总重量的增加而增加,但是与石头的数量无关。需要注意的是,代码中使用了一个固定大小的动态规划数组
dp
,这是因为题目给定了石头的最大数量为 30,每块石头的重量最大为 100。根据题目的限制条件,可以确定石头总重量的上限为 3000,因此背包容量的上限为 1500。为了保证数组dp
能够覆盖所有可能的背包容量,将其大小设置为 15001。如果题目的限制条件发生变化,可能需要调整数组dp
的大小以适应新的情况。
c++ 代码
int lastStoneWeightII(vector<int>& stones) {
vector<int> dp(15001, 0); // 创建一个大小为 15001 的动态规划数组 dp,初始值都为 0
int sum = 0; // 计算所有石头的总重量
for (int i = 0; i < stones.size(); i++) {
sum += stones[i];
}
int target = sum / 2; // 目标是将石头分为两堆,使得两堆的重量尽可能接近 sum/2
for (int i = 0; i < stones.size(); i++) {
for (int j = target; j >= stones[i]; j--) {
// 动态规划的核心逻辑
// dp[j] 表示容量为 j 的背包所能装载的最大重量
// dp[j-stones[i]]+stones[i] 表示将当前石头放入容量为 j 的背包中所能得到的重量
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return sum - dp[target] - dp[target]; // 返回两堆石头的重量差值
}
简洁写法(使用库函数做加法)
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
vector<int> dp(15001,0);
int sum = accumulate(stones.begin(), stones.end(), 0);
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];
}
};
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。