学习小组 I
将 n 名同学划分为若干个学习小组,若小组人数为 k 则产生 a_k 的积极度,要求求出所有划分方案中各小组积极度之和的最大值(本质为完全背包问题或线性动态规划问题)。
班主任计划将班级里的 名同学划分为若干个学习小组,每名同学都需要分入某一个学习小组中。观察发现,如果一个学习小组中恰好包含 名同学,则该学习小组的讨论积极度为 。
给定讨论积极度 ,请你计算将这 名同学划分为学习小组的所有可能方案中,讨论积极度之和的最大值。
输入格式
第一行,一个正整数 ,表示班级人数。
第二行, 个非负整数 ,表示不同人数学习小组的讨论积极度。
输出格式
输出共一行,一个整数,表示所有划分方案中,学习小组讨论积极度之和的最大值。
输入输出样例 #1
输入 #1
41 5 6 3输出 #1
10输入输出样例 #2
输入 #2
80 2 5 6 4 3 3 4输出 #2
12说明/提示
对于 的测试点,保证 。
对于所有测试点,保证 ,。
这是一道非常经典的动态规划(Dynamic Programming)入门题,属于“完全背包问题”的变种,即整数拆分(Integer Partition)最大化价值问题。
题目要求将 个人分成若干小组,每组人数 对应一个积极度 。我们要让所有小组的积极度之和最大。
-
状态定义: 设 表示将 名同学划分为若干小组所能获得的最大积极度之和。
-
状态转移方程: 要计算 ,我们可以考虑:最后一次划分出来的那个小组有多少人(设为 人,其中 )。那么此时的状态就是“前 个人的最大积极度”加上“最后一个 人小组的积极度”。
-
边界条件: (0 个人时积极度为 0)。
我们可以通过下表进行深入对比,理解其背后的算法模型:
| 维度 | 完全背包问题 | 本题(学习小组划分) |
|---|---|---|
| 总容量 | 背包的总承重 | 班级总人数 |
| 物品种类 | 不同重量和价值的物品 | 不同人数的小组(人组, 人组, …, 人组) |
| 物品“重量” | 物品重量 | 小组人数 |
| 物品“价值” | 物品价值 | 小组讨论积极度 |
| 选取限制 | 每种物品可无限次选择 | 每种人数的小组可无限次选择 |
从背包问题的视角看,我们实质上是有 种物品(对应 到 人组),每种物品都有其重量 和价值 。由于我们可以组建任意多个同类型的小组,这与“每种物品数量无限”的完全背包模型完美契合。
#include <iostream>#include <vector>#include <algorithm>
using namespace std;
int main() { int n; cin >> n;
// a[j] 存储 j 人小组的积极度,数组下标从 1 开始 vector<long long> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; }
// dp[i] 表示 i 个人能得到的最大积极度之和 vector<long long> dp(n + 1, 0);
for (int i = 1; i <= n; i++) { // 计算 i 个人时的最优解 for (int j = 1; j <= i; j++) { // 尝试最后一个小组的人数 j dp[i] = max(dp[i], dp[i - j] + a[j]); } }
cout << dp[n] << endl;
return 0;}-
时间复杂度: 我们有两层嵌套循环,外层 从 1 到 ,内层 从 1 到 。总计算量约为 ,即 。
由于 , ,在 1 秒的运行时间内完全可以通过。
-
空间复杂度: 我们使用了一个大小为 的数组,空间复杂度为 。