1 条题解

  • 0
    @ 2026-1-27 20:12:44
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    // [1] 精确设置dp数组最大容量:题目中背包总容积m≤10000,10005完全满足需求,提升CPU缓存命中率
    const int MAX_M = 10005;
    // [2] dp数组-动态规划状态,dp[j]表示容积为j的背包能装入的最大价值,全局数组默认初始化为0
    int dp[MAX_M] = {0};
    
    int main() {
        // 问题分析:混合背包终极优化版,解决最后一个超时测试点,包含完全背包、多重背包
        //          核心优化:1.多重背包转完全背包(c>m/a时数量足够多,等价无限);2.彻底用scanf/printf实现极致输入输出;
        //          3.过滤无效物品(体积超背包直接跳过);4.二进制拆分+即时01背包更新,无冗余存储/遍历
        // 解题步骤:1.读取物品种类数和背包总容积;2.遍历每个物品,先过滤体积超背包的无效物品;
        //          3.判断背包类型:c=0/ c>m/a → 完全背包(正序遍历);否则→多重背包(二进制拆分+即时01背包更新);
        //          4.最终dp[m]即为容积m的背包能装入的最大价值。
    
        // [3] n-物品的种类数;m-背包的总容积(最大可装体积)
        int n, m;
        scanf("%d%d", &n, &m);  // [4] 极致效率读取物品种类数和背包总容积
    
        // 遍历每一种物品,按类型分别处理(完全背包/多重背包)
        for (int i = 0; i < n; ++i) {
            // [5] a-原物品的单个体积;b-原物品的单个价值;c-原物品的可选数量(c=0表示完全背包/物品无限)
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);  // [6] 极致效率读取当前物品的体积、价值、数量
    
            if (a > m) continue;  // [7] 过滤无效物品:物品单个体积超过背包总容积,无法装入,直接跳过避免无意义计算
    
            if (c == 0 || c > m / a) {
                // [8] 处理完全背包:两种情况→①c=0(题目规定无限个);②c>m/a(数量足够多,最多装m/a个,等价无限)
                // 正序遍历容积:允许物品重复选取,dp[j-a]已包含选过当前物品的状态
                for (int j = a; j <= m; ++j) {
                    // [9] 完全背包状态转移:不选当前物品则dp[j]不变,选则取dp[j-a]+b,取两者最大值
                    dp[j] = max(dp[j], dp[j - a] + b);
                }
            } else {
                // [10] 处理多重背包(物品数量有限,且c≤m/a,需二进制拆分)→ 二进制拆分+即时01背包更新(无存储、无冗余)
                int k = 1;  // [11] 二进制拆分基数,按1、2、4、8...2^k的规律拆分,可组合出1~c的任意数量
                while (k <= c) {
                    int v = a * k;  // [12] 拆分出k个原物品作为虚拟物品,计算虚拟物品的总体积
                    int p = b * k;  // [13] 计算拆分后虚拟物品的总价值
                    // 逆序遍历容积:01背包核心,避免同一虚拟物品被多次选取
                    for (int j = m; j >= v; --j) {
                        // [14] 01背包即时状态转移:不选虚拟物品则dp[j]不变,选则取dp[j-v]+p,取最大值
                        dp[j] = max(dp[j], dp[j - v] + p);
                    }
                    c -= k;  // [15] 剩余可拆分的物品数量 = 原数量 - 已拆分的份数k
                    k *= 2;  // [16] 拆分基数翻倍,继续按2的幂次拆分
                }
                // 处理剩余未拆分的数量(不足2的幂次,单独作为一个虚拟物品)
                if (c > 0) {
                    int v = a * c;  // [17] 剩余c个原物品组成的虚拟物品总体积
                    int p = b * c;  // [18] 剩余c个原物品组成的虚拟物品总价值
                    // 逆序遍历容积,执行01背包即时更新
                    for (int j = m; j >= v; --j) {
                        // [19] 剩余虚拟物品的01背包状态转移
                        dp[j] = max(dp[j], dp[j - v] + p);
                    }
                }
            }
        }
    
        printf("%d\n", dp[m]);  // [20] 输出最终结果:容积为m的背包能装入的最大价值
        return 0;
    }
    
    • 1

    信息

    ID
    1336
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者