分组背包

点击查看代码
/*
分组背包

题目:https://www.luogu.com.cn/record/267074243

2.讲解算法原理

    1.状态表示  
        f[i][j] 表示:从前i个物品中挑选, 总重量不超过j的情况下, 最大价值----不行(不像之前的背包问题)
        f[i][j] 表示:从前i个小组中挑选, 总重量不超过j的情况下, 最大价值

    2.状态转移方程
                    不选 ----->     f[i - 1][j]
        f[i][j]     选一号 ---->    f[i - 1][j - a1] + b1   a-重量, b-价值
                    选二号------>   f[i - 1][j - a2] + b2
                    ............
    
    3.初始化:   全部为0 

    4.填表顺序:     从上到下每一行  

    5.最终结果

    6.空间优化:第二维必须从大到小
         删掉第一维数组即可
*/


#include <iostream>
#include <vector>

using namespace std;

// 定义pair别名,PII.first存物品重量,PII.second存物品价值,简化代码
typedef pair<int, int> PII;
// 数组最大容量:题目中m≤1000、组数≤100,1010足够覆盖所有情况
const int N = 1010;
// 分组存储物品:g[c]表示第c组的所有物品,每个物品用PII(重量, 价值)表示
vector<PII> g[N];
// 二维DP数组:f[i][j] 表示从前i个小组中挑选,总重量不超过j时的最大价值
int f[N][N];

int main(){
    // n:物品总数,m:背包最大容量(注意输入顺序是先m后n,匹配题目输入格式)
    int n, m;
    cin >> m >> n;
    
    // cnt:记录输入的最大组号(避免遍历无物品的空组,提升效率),初始为1(题目中组号从1开始)
    int cnt = 1;
    // 遍历所有物品,按组号分类存储
    for(int i = 1; i <= n; i++){
        // a:物品重量,b:物品价值,c:物品所属组号
        int a, b ,c;
        cin >> a >> b >> c;
        // 更新最大组号,确保后续遍历所有有物品的组
        cnt = max(cnt, c);
        // 将当前物品添加到对应组的列表中
        g[c].push_back({a, b});
    }
    
    // ========== 分组背包核心DP逻辑 ==========
    // 外层循环:遍历前i个小组(从上到下填表,匹配算法原理的填表顺序)
    for(int i = 1; i <= cnt; i++){
        // 中层循环:遍历背包容量j,从m到0逆序(空间优化的关键,避免同一组选多个物品)
        for(int j = m ; j >= 0; j--){
            // 状态转移-不选第i组:继承前i-1个小组、容量j的最大价值
            f[i][j] = f[i-1][j];
            
            // 内层循环:遍历第i组的所有物品,尝试选择其中一个
            for(auto& t: g[i]){
                // 取出当前物品的重量和价值
                int a = t.first;  // 物品重量
                int b = t.second; // 物品价值
                
                // 只有当背包容量j≥物品重量时,才有可能选择该物品
                if(j >= a){
                    // 状态转移-选第i组的当前物品:
                    // 比较「不选当前物品(当前f[i][j])」和「选当前物品(f[i-1][j-a]+b)」,取最大值
                    // 注:这里用f[i][j]而非f[i-1][j],是为了保留组内前一个物品的最优解,避免覆盖
                    f[i][j] = max(f[i][j], f[i-1][j-a] + b);
                }
            }
        }
    }
    
    // 最终结果:前cnt个小组、背包容量不超过m时的最大价值(匹配算法原理的最终结果)
    cout << f[cnt][m] << endl;
    return 0;
}
posted @ 2026-03-14 10:52  无酒无剑  阅读(32)  评论(0)    收藏  举报