当前位置:首页 > 牛客 > 牛客16949题:动态规划求解石头分组最小重量差问题

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

4周前 (08-19)

牛客16949题:动态规划求解石头分组最小重量差问题 动态规划 01背包 背包问题 C++ 状态转移方程 第1张

一、题目解读

牛客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模型对解决资源分配、分组优化问题有重要启发。



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣654:递归分治的艺术 如何用最大元素构建二叉树

力扣654:递归分治的艺术 如何用最大元素构建二叉树

题目重解我们面对一个看似简单却充满递归魅力的题目:给定一个不含重复元素的整数数组,需要构建一棵特殊的二叉树。这个树的每个父节点都必须是当前子数组中的最大元素,而它的左右子树则分别由该最大值左侧和右侧的...

NOIP2005 普及组 洛谷P1408 背包问题的空间优化技巧与实战应用

NOIP2005 普及组 洛谷P1408 背包问题的空间优化技巧与实战应用

题目重解想象你是一名药师,有t分钟在山上采集m种草药。每种草药需要time分钟采集,价值为num。这就像考试时分配时间做题,要选择收益最大的题目组合。题目要求计算在规定时间内能获得的最大草药价值。解题...

手搓顺序表类代码注释与详解:从零实现动态数组(新手教程)

一、简介和特点顺序表(Sequential List)是数据结构中基础的一种线性表,其特点是将数据元素存储在连续的内存空间中。通过数组实现,支持随机访问(即通过索引直接访问元素),适用于频繁随机读取的...

CSP-J方格取数题解|动态规划解法|洛谷P7074代码解析

CSP-J方格取数题解|动态规划解法|洛谷P7074代码解析

一、题目解读题目要求在一个n×m的网格中,从左上角到右下角选择一条路径,路径上的数字可重复取用,求取数之和的最大值。路径限制为仅能向右或向下移动。需注意路径的灵活性与重复取数的可能性,传统单向动态规划...

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...

1999年NOIP提高组导弹拦截(洛谷P1020)解题思路与动态规划代码解析

1999年NOIP提高组导弹拦截(洛谷P1020)解题思路与动态规划代码解析

一、题目解读    1999年NOIP提高组“导弹拦截”问题(对应洛谷P1020)要求设计导弹拦截系统:给定一组导弹高度数据,需计算最少拦截系统数量,并求最多能...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。