分组背包

  |  

摘要: 分组背包

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


本文我们以一个模板题看一下背包问题的变种之一:分组背包。分组背包是树形背包的基础,同时也是很多树形 DP 中状态转移的基本模型。

分组背包问题

有 N 组物品和一个容量是 V 的背包。

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

每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

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

输出最大价值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;

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

数据范围
0<N,V<=100
0<Si<=100
0<vij,wij<=100

输入样例
3 5
2
1 2
2 4
1
3 4
1
4 5
输出样例:
8

算法:动态规划

组数作为 DP 的阶段。转移始终是从第 i 组转移到第 i + 1 组,其中第 i 组只要选了一件物品,则必须要转移到第 i + 1 组。

1
2
3
4
5
6
7
8
9
10
状态定义
dp[i][j] := 前 i 组中选择总体积 j 的物品放入背包的最大价值

转移:
s[i] 表示第 i 组的物品个数
dp[i][j] = max (
dp[i - 1][j] (不选第 i 组的物品)
max(dp[i - 1][j - v[i][k]] + w[i][k]) 选择第 i 组的 k
其中 1 <= k <= s[i]
)

代码 (C++)

注意循环顺序:k 在 j 里面,因为每组最多选一个。

结合概念理解循环顺序:i 是阶段;i, j 共同构成状态;k 是决策

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <vector>
#include <iostream>

using namespace std;

int main()
{
int N;
int V;
cin >> N >> V;

vector<vector<int> > v(N, vector<int>());
vector<vector<int> > w(N, vector<int>());

for(int i = 0; i < N; ++i)
{
int s;
cin >> s;
for(int j = 0; j < s; ++j)
{
int vv, ww;
cin >> vv >> ww;
v[i].push_back(vv);
w[i].push_back(ww);
}
}

vector<int> dp(V + 1, 0);
for(int i = 1; i <= N; ++i)
{
int s = v[i - 1].size();
for(int j = V; j >= 0; --j)
for(int k = 0; k < s; ++k)
if(j >= v[i - 1][k])
dp[j] = max(dp[j], dp[j - v[i - 1][k]] + w[i - 1][k]);
}

cout << dp[V] << endl;
return 0;
}

Share