分组背包(18-18)

81 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 18 天,点击查看活动详情

本题为分组背包的模板题。

分组背包

题目描述

有 NN 组物品和一个容量是 VV 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。

每件物品的体积是 vijv_{ij},价值是 wijw_{ij},其中 ii 是组号,jj 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 NVN,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 NN 组数据:

  • 每组数据第一行有一个整数 SiS_i,表示第 ii 个物品组的物品数量;
  • 每组数据接下来有 SiS_i 行,每行有两个整数 vij,wijv_{ij},w_{ij},用空格隔开,分别表示第 ii 个物品组的第 jj 个物品的体积和价值;

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V1000<N,V≤100

0<si1000<s_i≤100

0<vij,wij1000<v_{ij},w_{ij}≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例

8

题目分析

这是一道分组背包的板子题。

在我看来,我认为可以用01背包的思想帮助理解分组背包。

在01背包中,每件物品有两种状态,选或不选。同样的,在分组背包中,每组物品也同样有两种状态,一是不选,二是选择其中最优的物品,而这个状态又有 SiS_i 个子状态,即选择第 ii 件物品。

我们设 f[i][j]f[i][j] 为前 ii 组物品中总体积为 jj 的最优解。

f[i][j]=max(f[i1][j],  f[i1][jvi1]+wi1,  f[i1][jvi2]+wi2,  f[i1][jvik]+wik)f[i][j]=max(f[i-1][j],\; f[i-1][j-v_{i1}]+w_{i1},\; f[i-1][j-v_{i2}]+w_{i2},\; \dots f[i-1][j-v_{ik}]+w_{ik})

同样也可以优化到一维,复杂度为 O(n2v)O(n^2v)

Accept代码 O(n^2v)

#include <bits/stdc++.h>

using namespace std;

const int N = 110;
int v[N][N], w[N][N];
int s[N], f[N];

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++)
    {
        cin >> s[i];
        for (int j = 1; j <= s[i]; j ++) cin >> v[i][j] >> w[i][j];
    }
    
    for (int i = 1; i <= n; i ++)
        for (int k = m; k; k --)
            for (int j = 1; j <= s[i]; j ++)    // 此层循环和上层不能交换
                if (k >= v[i][j]) f[k] = max(f[k], f[k - v[i][j]] + w[i][j]);
    cout << f[m];
    return 0;
}