【题目链接】
ybt 4164:【GESP2512七级】学习⼩组
洛谷 P14922 [GESP202512 七级] 学习小组
【题目难度】:B
【题目考点】
1. 动态规划:背包问题
【解题思路】
解法1(非正解):利用特殊性质 c i = 0 c_i=0 ci=0
观察该问题的特殊性质:"对于 40% 的测试点,保证 c i = 0 " c_i =0" ci=0",此时该题与
洛谷 P13015 [GESP202506 六级] 学习小组相同,使用上题的解法即可得至少到40分。
解法2:状态定义: i i i个数有 j j j个元素个数大于等于2的子集的最大贡献。
每个人的 c i c_i ci可以看作一个元素,所有元素构成一个集合。一个学习小组可以看作由 c i c_i ci构成的全集的一个子集。这个子集对"讨论积极度之和"的贡献为这个集合的元素个数 k k k(人数)对应的 a k a_k ak以及这个集合中元素的极差(最大值减最小值)。
假设当前已经确定了每个子集的元素个数,那么基础讨论积极度 a a a对于"讨论积极度之和"的影响已经固定了,还需要再加上每个集合中元素极差。
要想使"讨论积极度之和"最大,那么每个集合中元素的极差应该达到最大。
如果一个集合中只有1个元素,则该集合中元素的极差为0,对结果没有贡献。因此只考虑元素个数大于等于2的集合。
设第 i i i个集合的最大值为 c i m a x c_{i_{max}} cimax,最小值为 c i m i n c_{i_{min}} cimin,假设 n n n个元素分成的子集中有 m m m个元素数量大于等于2的子集,那么集合极差对结果的贡献为:
∑ i = 1 m ( c i m a x − c i m i n ) = ∑ i = 1 m c i m a x − ∑ i = 1 m c i m i n \sum\limits_{i=1}^m(c_{i_{max}}-c_{i_{min}})=\sum\limits_{i=1}^mc_{i_{max}}-\sum\limits_{i=1}^mc_{i_{min}} i=1∑m(cimax−cimin)=i=1∑mcimax−i=1∑mcimin
要想使该值最大,自然需要让 ∑ i = 1 m c i m a x \sum\limits_{i=1}^mc_{i_{max}} i=1∑mcimax达到最大,让 ∑ i = 1 m c i m i n \sum\limits_{i=1}^mc_{i_{min}} i=1∑mcimin达到最小。
因此:
- 将所有元素 c i c_i ci中最大的 m m m个数作为元素个数大于等于2的各集合中的最大值 c 1 m a x , . . . , c m m a x c_{1_{max}}, ..., c_{m_{max}} c1max,...,cmmax
- 将所有元素 c i c_i ci中最小的 m m m个数作为元素个数大于等于2的各集合中的最小值 c 1 m i n , . . . , c m m i n c_{1_{min}}, ..., c_{m_{min}} c1min,...,cmmin
因此,
第一个确定的元素数量大于等于2的集合,这个集合中应该包含 c c c序列中的最大值和最小值。
第二个确定的元素数量大于等于2的集合,这个集合中应该包含 c c c序列中的次大值和次小值。
...
第 m m m个确定的元素数量大于等于2的集合,这个集合中应该包含 c c c序列中的第 m m m大的元素和第 m m m小的元素。
这样就可以保证各集合了极差对结果的贡献 ∑ i = 1 m ( c i m a x − c i m i n ) \sum\limits_{i=1}^m(c_{i_{max}}-c_{i_{min}}) i=1∑m(cimax−cimin)是最大的。
状态定义
- 阶段: i i i个数,有 j j j个元素个数大于等于2的子集
- 决策:将几个数分成一个子集
- 策略:子集划分方案
- 策略集合: i i i个数用了 j j j对最值进行子集划分的所有方案
- 条件:贡献最大
- 统计量:贡献
状态定义 d p i , j dp_{i,j} dpi,j: i i i个数有 j j j个元素个数大于等于2的子集进行子集划分的所有方案中,贡献最大的方案的贡献。
初始状态: 0 0 0个数进行子集划分,使用 0 0 0对最值,贡献为0,所以 d p 0 , 0 = 0 dp_{0,0}=0 dp0,0=0
如果没有元素个数大于等于2的集合,即 j = 0 j=0 j=0,那么前 i i i个数进行集合划分的最大贡献为前 i − 1 i-1 i−1个数进行集合划分的最大贡献,再加上1个数构成集合产生的贡献 a 1 a_1 a1,即 d p i , 0 = d p i − 1 , 0 + a 1 dp_{i,0}=dp_{i-1,0}+a_1 dpi,0=dpi−1,0+a1,
状态转移方程
- 策略集合: i i i个数有 j j j个元素个数大于等于2的子集进行子集划分的所有方案
- 分割策略集合:根据最后一个集合的元素数量分割策略集合
元素数量大于等于2的子集数量最多为总元素数量 i i i的一半,即 j ≤ ⌊ i 2 ⌋ j\le \lfloor \frac{i}{2}\rfloor j≤⌊2i⌋
如果最后一个分出的子集有 k k k个元素,那么该子集元素个数对结果的贡献为 a k a_k ak,集合中元素数 k k k最小为1,最大时为当前的元素总数 i i i,因此 1 ≤ k ≤ i 1\le k\le i 1≤k≤i。
- 如果 k = 1 k=1 k=1,那么剩下 i − 1 i-1 i−1个数已用 j j j对最值进行子集划分能得到的最大贡献为 d p i − 1 , j dp_{i-1, j} dpi−1,j,总贡献为 d p i − 1 , j + a k dp_{i-1,j}+a_k dpi−1,j+ak
- 如果 k > 1 k>1 k>1,那么该集合中会用到第 j j j对最值,也就是 c c c序列在进行升序排序后的第 j j j元素 c j c_{j} cj,和倒数第 j j j元素,也就是正数第 n − j + 1 n-j+1 n−j+1元素 c n − j + 1 c_{n-j+1} cn−j+1。剩下 i − k i-k i−k个数有 j − 1 j-1 j−1个元素个数大于等于2的子集进行子集划分能获得的最大贡献为 d p i − k , j − 1 dp_{i-k,j-1} dpi−k,j−1,总贡献为 d p i − k , j − 1 + a k + c n − j + 1 − c j dp_{i-k, j-1}+a_k+c_{n-j+1}-c_j dpi−k,j−1+ak+cn−j+1−cj
- 对于 k k k的每种取值,求贡献的最大值。
因此状态转移方程为:
d p i , j = { d p i − k , j + a k , k = 1 d p i − k , j − 1 + a k + c n − j + 1 − c j , 2 ≤ k ≤ i dp_{i,j} = \begin{cases} dp_{i-k,j}+a_k, & k=1\\ dp_{i-k, j-1}+a_k+c_{n-j+1}-c_j, & 2\le k \le i \end{cases} dpi,j={dpi−k,j+ak,dpi−k,j−1+ak+cn−j+1−cj,k=12≤k≤i
本题结果为:对 n n n个人进行分组,元素数量大于等于2的子集数量从 0 0 0到 n 2 ⌋ \frac{n}{2}\rfloor 2n⌋,可以得到的最大讨论积极度,即 max { d p n , j } , 0 ≤ j ≤ ⌊ n 2 ⌋ \max\{dp_{n,j}\}, 0\le j\le \lfloor \frac{n}{2}\rfloor max{dpn,j},0≤j≤⌊2n⌋
时间复杂度为 ( n 3 ) (n^3) (n3)
解法3:状态定义: i i i个数分为 j j j个子集的最大贡献。
状态定义 d p i , j dp_{i,j} dpi,j: i i i个数分为 j j j个子集进行子集划分的所有方案中,贡献最大的方案的贡献。
状态转移方程:
- 策略集合: i i i个数有 j j j子集进行子集划分的所有方案
- 分割策略集合:根据最后一个集合的元素数量分割策略集合
设最后一个集合的元素数量为 k k k
- 如果 k = 1 k=1 k=1,那么该集合的极差没有贡献,元素数量的贡献为 a k a_k ak,剩下 i − 1 i-1 i−1个元素分为 j − 1 j-1 j−1个集合的最大贡献为 d p i − 1 , j − 1 dp_{i-1,j-1} dpi−1,j−1,总贡献为 d p i − 1 , j − 1 + a k dp_{i-1,j-1}+a_k dpi−1,j−1+ak
- 如果 k > 1 k>1 k>1,那么该集合元素数量的贡献为 a k a_k ak,极差的贡献为 c n − j + 1 − c j c_{n-j+1}-c_j cn−j+1−cj剩下 i − k i-k i−k个元素分成 j − 1 j-1 j−1个集合的最大贡献为 d p i − 1 , j − 1 dp_{i-1,j-1} dpi−1,j−1,总贡献为 d p i − 1 , j − 1 + a k + c n − j + 1 − c j dp_{i-1,j-1}+a_k+c_{n-j+1}-c_j dpi−1,j−1+ak+cn−j+1−cj。
- 以上情况取最大值。
因此状态转移方程为:
d p i , j = { d p i − k , j − 1 + a k , k = 1 d p i − k , j − 1 + a k + c n − j + 1 − c j , 2 ≤ k ≤ i dp_{i,j} = \begin{cases} dp_{i-k,j-1}+a_k, & k=1\\ dp_{i-k, j-1}+a_k+c_{n-j+1}-c_j, & 2\le k \le i \end{cases} dpi,j={dpi−k,j−1+ak,dpi−k,j−1+ak+cn−j+1−cj,k=12≤k≤i
本题结果为: max { d p n , j } , 1 ≤ j ≤ n \max\{dp_{n,j}\}, 1\le j\le n max{dpn,j},1≤j≤n
【题解代码】
解法1(非正解):利用特殊性质 c i = 0 c_i=0 ci=0,55pt
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
int n, c[N], a[N], dp[N];//dp[i]:不考虑c的影响,i个人能获得的最大积极度
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> c[i];
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
dp[i] = max(dp[i], dp[i-j]+a[j]);
cout << dp[n];
return 0;
}
解法2:状态定义: i i i个数有 j j j个元素个数大于等于2的子集的最大贡献。
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
int n, c[N], a[N], dp[N][N], ans;//dp[i][j]:i个人使用j对最值能获得的最大贡献
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> c[i];
sort(c+1, c+1+n);
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int i = 1; i <= n; ++i)
dp[i][0] = dp[i-1][0]+a[1];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i/2; ++j)
{
dp[i][j] = dp[i-1][j]+a[1];//k=1的情况
for(int k = 2; k <= i; ++k)
dp[i][j] = max(dp[i][j], dp[i-k][j-1]+a[k]+c[n-j+1]-c[j]);
}
for(int j = 0; j <= n/2; ++j)
ans = max(ans, dp[n][j]);
cout << ans;
return 0;
}
解法3:状态定义: i i i个数分为 j j j个子集的最大贡献。
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
int n, c[N], a[N], dp[N][N], ans;//dp[i][j]:前i个元素分成j个集合的最大贡献
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> c[i];
sort(c+1, c+1+n);
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
{
dp[i][j] = dp[i-1][j]+a[1];//k=1的情况
for(int k = 2; k <= i; ++k)//分出的集合元素数
dp[i][j] = max(dp[i][j], dp[i-k][j-1]+a[k]+c[n-j+1]-c[j]);
}
for(int j = 1; j <= n; ++j)
ans = max(ans, dp[n][j]);
cout << ans;
return 0;
}