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



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣35:二分法在搜索插入位置中的运用

力扣35:二分法在搜索插入位置中的运用

有序数组的定位在一个严格递增的数字序列中,每个元素都有其确定的位置。当新元素试图加入时,我们需要回答两个问题:它是否已经存在?如果不存在,它应该插入在哪里?这道题要求我们在O(log n)时间内完成这...

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

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

NOIP 2008火柴棒等式题解(C++代码实现)  动态规划与枚举算法详解

NOIP 2008火柴棒等式题解(C++代码实现) 动态规划与枚举算法详解

一、题目解读火柴棒等式问题(NOIP 2008,洛谷P1149)要求使用给定数量的火柴棒,构造形如 A + B = C 的等式,其中A、B、C均为整数,且火柴棒总数恰好等于输入值。需统计符合条件的等式...

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

一、题目解读2024年蓝桥杯省B组“传送阵”题目要求处理一个包含n个节点的图,节点间存在单向传输关系。每个节点i可传送至a[i]指定的节点,形成可能存在的环结构。题目需求解从任意节点出发能到达的最长路...

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

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

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

洛谷2789题解:直线交点数的递归求解与优化(附代码详解)

洛谷2789题解:直线交点数的递归求解与优化(附代码详解)

一、题目解读洛谷2789题要求计算n条直线在平面上两两相交时产生的不同交点数量。题目强调“不同”交点,需排除重复情况。解题关键在于如何高效枚举所有可能的交点组合,并避免重复计数。二、解题思路参考代码采...

发表评论

访客

看不清,换一张

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