1 条题解

  • 0
    @ 2026-1-27 19:59:05
    #include <bits/stdc++.h>
    using namespace std;
    
    int n,m;                 // [1] n-待选物品总数;m-总拨款金额(元)
    struct stu{
        int v,p,num;         // [2] v-虚拟物品的总价格;p-虚拟物品的总价值;num-该虚拟物品对应的原物品数量
    }arr[1001000];           // [3] arr数组-存储二进制拆分后的所有“虚拟物品”
    int dp[1001000]={};      // [4] dp数组-动态规划状态,dp[j]表示用j元钱能买到的最大价值
    
    int main() {
        // 问题分析:多重背包问题,每种物品有价格、价值和购买数量限制,求m元能买的最大价值
        // 解题步骤:用二进制拆分将多重背包转化为01背包(把原物品的数量拆成1,2,4,...等份的虚拟物品),再用01背包求解
        
        cin>>n>>m;  // [5] 读取待选物品数n和总拨款m
        int x=1,a,b,c;  // [6] x-虚拟物品的计数指针;a-原物品的单价;b-原物品的单位价值;c-原物品的最大购买数量
        
        // 遍历每种物品,进行二进制拆分
        for(int i=1;i<=n;i++){
            cin>>a>>b>>c;  // [7] 读取当前物品的单价a、单位价值b、最大购买数量c
            // 二进制拆分:把c件物品拆成1,2,4,...等份,每份作为一个虚拟物品
            int j=1;
            while(j<=c){
                arr[x].v = a*j;  // [8] 虚拟物品的总价格 = 单价 * 份数j
                arr[x].p = b*j;  // [9] 虚拟物品的总价值 = 单位价值 * 份数j
                arr[x++].num = j;  // [10] 记录该虚拟物品对应的原物品数量,x指针后移
                c -= j;  // [11] 剩余数量减去已拆分的份数j
                j *= 2;  // [12] 份数翻倍,继续拆分
            }
            if(c){  // [13] 如果还有剩余数量(不足2的幂次),单独作为一个虚拟物品
                arr[x].v = a*c;
                arr[x].p = b*c;
                arr[x++].num = c;
            }
        }
        
        // 01背包求解:遍历所有虚拟物品,逆序更新dp数组
        dp[0]=0;  // [14] 初始化:0元钱能买0价值
        for(int i=1;i<x;i++){  // [15] 遍历所有二进制拆分后的虚拟物品
            for(int j=m;j>=arr[i].v;j--){  // [16] 逆序遍历资金,避免重复选择同一虚拟物品
                // 状态转移:不选当前物品时dp[j]不变;选当前物品时,dp[j-arr[i].v] + arr[i].p
                dp[j] = max(dp[j], dp[j-arr[i].v] + arr[i].p);  // [17] 取两种情况的最大值
            }
        }
        cout<<dp[m];  // [18] 输出m元能买到的最大价值
        return 0;
    }
    
    • 1

    信息

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