【题目链接】
【题目考点】
1. 动态规划:分组背包
【解题思路】
分组背包问题,特点是:每组中的物品最多可以取一件
1. 状态定义
集合:放入背包的物品方案
限制:物品所在分组的区间,背包大小
属性:价值
条件:最大
统计量:价值
状态定义:
dp[i][j]
: 在前i组物品中选择物品放入大小为j的背包能获得的最大价值。
2. 状态转移方程
记第i组物品有m个,这m个物品的编号为i1,i2,…,im。
第i物品的重量是w[i]
,价值是c[i]
。
集合:在前i组物品中选择物品放入大小为j的背包的所有方案
分割集合:第i组物品如何放入背包
- 如果不选择第i组物品,那么在剩余i-1组中选择物品放入大小为j的背包。
dp[i][j] = dp[i-1][j]
- 如果选择第i组物品中的第1个,该物品下标为i1。那么需要在剩余i-1组中选择物品放入大小为
j-w[i1]
的背包。dp[i][j] = dp[i-1][j-w[i1]]+c[i1]
- 如果选择第i组物品中的第2个,该物品下标为i2。那么需要在剩余i-1组中选择物品放入大小为
j-w[i2]
的背包。dp[i][j] = dp[i-1][j-w[i2]]+c[i2]
… - 如果选择第i组物品中的第m个,该物品下标为im。那么需要在剩余i-1组中选择物品放入大小为
j-w[im]
的背包。dp[i][j] = dp[i-1][j-w[im]]+c[im]
- 一般地,如果选择第i组物品中的第k个,该物品下标为x。那么需要在剩余i-1组中选择物品放入大小为
j-w[x]
的背包。dp[i][j] = dp[i-1][j-w[x]]+c[x]
。遍历第i组物品,求选择该组中每个物品后能得到的最大价值dp[i][j]
。 - 以上所有情况求最大值。
3. 复杂度分析
背包容量为V,物品总数量为N,分组数量为T。
分组背包一般解法:
- 时间复杂度: O ( V N ) O(VN) O(VN)
- 空间复杂度: O ( T V ) O(TV) O(TV)
滚动数组优化后:
- 空间复杂度: O ( V ) O(V) O(V)
【题解代码】
解法1:分组背包 一般解法
#include<bits/stdc++.h>
using namespace std;
#define V 205
#define N 35
#define T 15
int dp[N][V], w[N], c[N]; //dp[i][j]:在前i组物品中选择物品放入大小为j的背包能获得的最大价值。
vector<int> g[T];//g[i]:存储第i组物品的编号
int main()
{
int v, n, t, p;
cin >> v >> n >> t;
for(int i = 1; i <= n; ++i)
{
cin >> w[i] >> c[i] >> p;
g[p].push_back(i);//第p组添加i
}
for(int i = 1; i <= t; ++i)//遍历各组
{
for(int j = 0; j <= v; ++j)
{
dp[i][j] = dp[i-1][j];
for(int k = 0; k < g[i].size(); ++k)
{
int x = g[i][k];
if(w[x] <= j)
dp[i][j] = max(dp[i][j], dp[i-1][j-w[x]]+c[x]);
}
}
}
cout << dp[t][v];
return 0;
}
解法2:分组背包 滚动数组优化
#include<bits/stdc++.h>
using namespace std;
#define V 205
#define N 35
#define T 15
int dp[V], w[N], c[N]; //dp[i][j]:(第1维被优化掉)在前i组物品中选择物品放入大小为j的背包能获得的最大价值。
vector<int> g[T];//g[i]:存储第i组物品的编号
int main()
{
int v, n, t, p;
cin >> v >> n >> t;
for(int i = 1; i <= n; ++i)
{
cin >> w[i] >> c[i] >> p;
g[p].push_back(i);//第p组添加i
}
for(int i = 1; i <= t; ++i)//遍历各组
{
for(int j = v; j >= 0; --j)
{
for(int k = 0; k < g[i].size(); ++k)
{
int x = g[i][k];
if(j >= w[x])
dp[j] = max(dp[j], dp[j-w[x]]+c[x]);
}
}
}
cout << dp[v];
return 0;
}