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



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣70题:告别暴力递归!从零实现记忆化搜索解法

力扣70题:告别暴力递归!从零实现记忆化搜索解法

题意解析:想象你站在楼梯底部,面前有n级台阶。每次你可以选择跨1级或2级台阶,最终到达顶端的路径有多少种不同的走法?这个问题本质上是在探索分叉决策的叠加效果——当我们把每个台阶处的选择看作二叉树的分支...

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...

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

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

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

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

一、题目解读力扣931题「Minimum Falling Path Sum」(最小下降路径和)要求在一个n x n的整数矩阵中,计算从顶部到底部的最小路径和。路径只能从每个位置向下或对角线移动(即向下...

牛客网288555题解题指南:动态规划求解小红的暑假(附代码解析)

牛客网288555题解题指南:动态规划求解小红的暑假(附代码解析)

一、题目解读牛客网288555题要求解决一个组合数学问题:有三位朋友,每天需邀请其中一位参加聚会,但不能连续两天邀请同一位朋友。给定天数n,求满足条件的不同邀请方案总数。题目考察动态规划、状态转移及组...

发表评论

访客

看不清,换一张

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