牛客16949题:动态规划求解石头分组最小重量差问题

一、题目解读
牛客16949题要求将一组石头分成两部分,使两组的重量差最小。题目给出一个整数数组stones,每个元素代表一块石头的重量,需找到一种分组方案,使得两组总重量之差绝对值最小。例如,当输入[5,1,1,1,1,1]时,最优分组为[5]和[1,1,1,1,1],重量差为0。
二、解题思路
采用动态规划(DP)解决该问题,核心思想是将问题转化为“能否用部分石头组成目标重量”。具体策略如下:
1. 总重量计算:先求所有石头总重量total,目标为找到最接近total/2的重量组合。
2. 动态规划:定义布尔型DP数组dp[i]表示是否存在一种方案使石头组合重量为i。
3. 状态转移:从大到小遍历每个石头重量stone,若dp[i-stone]为真,则更新dp[i]为真(即当前石头可加入已有组合)。
4. 寻找最优解:反向遍历dp数组,找到第一个满足dp[i]且i接近total/2的值,该值即为最大可组合重量,剩余重量即为另一组重量。
三、解题步骤
1. 初始化:
计算总重量total并取半(half = total / 2)。
创建DP数组dp[0..half],初始化dp[0]=true(空组合重量为0)。
2. 填充DP数组:
外层循环遍历每个石头stone。
内层倒序循环i=half..stone,若dp[i-stone]为真,则更新dp[i]为真(表示可组合)。
3. 寻找最接近目标重量:
反向遍历dp数组,找到第一个dp[i]为真的i,记为max_weight。
另一组重量为total - max_weight。
4. 返回结果:取两组重量中较大和较小的值,形成答案数组。
四、代码与注释
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
// 核心函数:计算石头分组的最小重量差
vector<int> partitionStones(vector<int>& stones) {
int total = accumulate(stones.begin(), stones.end(), 0); // 总重量
int half = total / 2; // 目标半重量
int n = stones.size();
// dp[i]:是否存在组合重量为i
vector<bool> dp(half + 1, false);
dp[0] = true; // 空组合重量为0,默认成立
// 动态规划填充
for (int stone : stones) { // 遍历每块石头
for (int i = half; i >= stone; --i) { // 倒序遍历可能的重量
if (dp[i - stone]) { // 若可减去该石头形成i
dp[i] = true; // 更新状态
}
}
}
// 寻找最接近half的可组合重量
int max_weight = 0;
for (int i = half; i >= 0; --i) { // 反向查找第一个true
if (dp[i]) {
max_weight = i;
break;
}
}
int other = total - max_weight; // 另一组重量
return {max(other, max_weight), min(other, max_weight)}; // 返回较大和较小重量
}
int main() {
vector<int> stones;
int num;
char comma;
// 输入处理:逗号分隔的石头重量
while (cin >> num) {
stones.push_back(num);
if (cin.peek() == ',') { // 检查逗号分隔符
cin >> comma;
} else {
break;
}
}
vector<int> result = partitionStones(stones);
cout << result[0] << "," << result[1] << endl; // 输出结果
return 0;
}五、总结
本解法通过动态规划将问题转化为01背包变种,巧妙利用反向查找优化复杂度。核心在于理解“最小重量差”等价于“是否存在接近总重量一半的组合”,时间复杂度为O(n*sum),空间为O(sum)。掌握此类DP模型对解决资源分配、分组优化问题有重要启发。
原创内容 转载请注明出处






