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

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

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



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣746:三步通关最小花费爬楼梯

力扣746:三步通关最小花费爬楼梯

题目解析:站在楼梯的某个台阶时,需要支付当前台阶对应的体力值cost[i],之后可以选择向上爬1或2个台阶。最终目标是到达‌楼层顶部‌(即数组末尾之后的位置),且初始位置可选择下标0或1的台阶作为起点...

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

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

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

洛谷P4999题解析:动态规划求解数字拆分与求和问题(附代码)

洛谷P4999题解析:动态规划求解数字拆分与求和问题(附代码)

一、题目解读洛谷P4999题要求处理给定区间 [L, R] 内数字的拆分与求和问题。每个数字需拆分为其各位数字之和,并计算区间内所有数字之和的累加结果。题目需考虑大数情况,并采用取模运算(MOD=1e...

标题:洛谷B3617题解析:八进制转十六进制算法实现与优化(附AC100代码)

标题:洛谷B3617题解析:八进制转十六进制算法实现与优化(附AC100代码)

一、题目解读洛谷B3617题要求将输入的八进制字符串转换为十六进制表示。题目需处理大数场景,且对输入合法性有明确限制(长度不超过1000,仅包含0-7字符)。由于八进制与十六进制无法直接转换,需借助十...

牛客4580题解:动态规划求解网格路径概率问题(C++代码实现)

牛客4580题解:动态规划求解网格路径概率问题(C++代码实现)

一、题目解读牛客4580题要求在一个n×m的网格中计算从起点(1,1)到终点(n,m)的概率。网格中存在障碍物(标记为坏点),路径只能向右或向下移动。到达终点时,若处于边界位置,概率转移规则不同:下边...

【2020蓝桥杯国赛C组】补给题解析:从Floyd到动态规划的高效解法

【2020蓝桥杯国赛C组】补给题解析:从Floyd到动态规划的高效解法

一、题目解读2020年蓝桥杯国赛C组“补给”题目要求:给定n个村庄坐标及最大补给距离D,需判断是否所有村庄均可从总部(村庄0)直接或间接到达,并计算访问所有村庄的最小路径(即旅行商问题TSP)。题目核...

发表评论

访客

看不清,换一张

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