蓝桥杯C++基础算法-分组背包
这段代码实现了一个分组背包问题的动态规划解法。与之前的多重背包问题不同,这里的每个物品有多个不同的体积和价值组合,而不是单一的体积和价值。以下是代码的详细思路解析:
1. 问题背景
给定 n 个物品组,每个物品组有 s[i] 个不同的物品,每个物品有其体积 v[i][j] 和价值 w[i][j],以及一个容量为 m 的背包。目标是选择物品使得总价值最大,同时总容量不超过背包的容量。
2. 动态规划的概念
动态规划是一种常用的算法技巧,用于解决具有重叠子问题和最优子结构的问题。在多重背包问题中,动态规划通过维护一个一维数组 f 来记录不同状态下的最大价值。
3. 代码逻辑解析
(1) 输入数据
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
for (int j = 0; j < s[i]; j++)
cin >> v[i][j] >> w[i][j];
}
-
用户输入物品组数量
n和背包容量m。 -
对于每个物品组,输入该组的物品数量
s[i],以及每个物品的体积v[i][j]和价值w[i][j]。
(2) 动态规划状态转移
for (int i = 1; i <= n; i++)
for (int j = m; j >= 0; j--)
for (int k = 0; k < s[i]; k++)
if (v[i][k] <= j)
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
-
外层循环:
-
遍历每个物品组,从第 1 个到第
n个。
-
-
中层循环:
-
遍历背包的每个容量,从
m到 0(逆序遍历)。 -
逆序遍历的原因是避免重复使用同一个物品。如果正序遍历,同一个物品可能会被多次使用,从而变成完全背包问题。
-
-
内层循环:
-
遍历每个物品组中的每个物品,从 0 到
s[i] - 1。
-
-
状态转移:
-
f[j]表示在容量为j的背包下的最大价值。 -
不选择第
i个物品组中的第k个物品:f[j]保持不变。 -
选择第
i个物品组中的第k个物品:如果当前容量j大于等于第k个物品的体积v[i][k],则可以考虑选择该物品,更新f[j]为f[j - v[i][k]] + w[i][k],即在容量为j - v[i][k]的背包下的最大价值加上第k个物品的价值。
-
(3) 输出结果
cout << f[m] << endl;
-
输出最终的最大价值,即
f[m]。
4. 代码效率分析
-
时间复杂度:
-
三层循环遍历所有物品组、所有容量和每个物品组中的每个物品,时间复杂度为 O(n × m × max(s[i])),其中
max(s[i])是最大的物品组中的物品数量。
-
-
空间复杂度:
-
使用了一个一维数组
f,空间复杂度为 O(m)。
-
5. 示例运行
输入:
3 5
2
1 2
2 4
1
3 4
1
4 5
输出:
8
6. 总结
这段代码的核心思路是通过动态规划解决多重背包问题。通过维护一个一维数组 f,记录不同状态下的最大价值,并通过状态转移方程更新最大价值,最终找到在给定背包容量下的最大价值。这种方法的时间复杂度为 O(n × m × max(s[i])),空间复杂度为 O(m),适用于中等规模的多重背包问题。
完整代码
#include<bits/stdc++.h>
using namespace std;
// 定义常量 N 作为数组的最大长度
const int N = 110;
// n 表示物品组的数量,m 表示背包的容量
int n, m;
// v 数组存储每个物品组内物品的体积,v[i][j] 表示第 i 个物品组中第 j 个物品的体积
// w 数组存储每个物品组内物品的价值,w[i][j] 表示第 i 个物品组中第 j 个物品的价值
// s 数组存储每个物品组内物品的数量,s[i] 表示第 i 个物品组中物品的数量
int v[N][N], w[N][N], s[N];
// f 数组是一维数组,f[j] 表示背包容量为 j 时能获得的最大价值
int f[N];
int main()
{
// 输入物品组的数量 n 和背包的容量 m
cin >> n >> m;
// 循环读入每个物品组的信息
for(int i = 1; i <= n; i ++)
{
// 输入第 i 个物品组中物品的数量
cin >> s[i];
// 循环读入第 i 个物品组中每个物品的体积和价值
for(int j = 0; j < s[i]; j ++)
cin >> v[i][j] >> w[i][j];
}
// 动态规划过程,外层循环遍历每个物品组
for(int i = 1; i <= n; i ++)
// 中层循环从背包的最大容量 m 开始,递减到 0,这是为了保证每个物品组最多选一个物品
for(int j = m; j >= 0; j --)
// 内层循环遍历当前物品组中的每个物品
for(int k = 0; k < s[i]; k ++)
// 如果当前物品的体积小于等于当前背包的剩余容量
if(v[i][k] <= j)
// 比较不选择当前物品组的物品和选择当前物品组中第 k 个物品两种情况下的最大价值
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
// 输出背包容量为 m 时能获得的最大价值
cout << f[m] << endl;
return 0;
}
更多推荐
所有评论(0)