选元素

选元素

给定一个长度为 n 的整数序列 a1,a2,,an

请你从中挑选 x 个元素,要求:

  1. 原序列中的每一个长度为 k 的连续子序列都至少包含一个被选中的元素。
  2. 满足条件 1 的前提下,所选 x 个元素的相加之和应尽可能大。

输出最大可能和。

输入格式

第一行包含三个整数 n,k,x

第二行包含 n 个整数 a1,a2,,an

输出格式

如果无法满足题目要求,则输出 1

否则,输出一个整数,表示所选元素的最大可能和。

数据范围

前三个测试点满足 1k,xn6
所有测试点满足 1k,xn2001ai109

输入样例1:

5 2 3
5 1 3 10 1

输出样例1:

18

输入样例2:

6 1 5
10 30 30 70 10 10

输出样例2:

-1

输入样例3:

4 3 1
1 100 1 1

输出样例3:

100

 

解题思路

  首先可以发现这题的选法有很多,大概是指数级别的。例如,如果k=n,那么我们的选法就有Cnx种,那么这题大概率用动态规划或者贪心来做。

  最后的答案应该是max{f(n,x),f(n1,x),,f(nk+1,x)},因为最后一个数n不一定会被选,而且因为要求长度为k的子序列中至少要有1个数被选,因此最远可以选到第ik+1个数。

  时间复杂度为O(n3),AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 const int N = 210;
 9 
10 LL a[N], f[N][N];
11 
12 int main() {
13     int n, k, m;
14     scanf("%d %d %d", &n, &k, &m);
15     for (int i = 1; i <= n; i++) {
16         scanf("%d", a + i);
17     }
18     
19     // 处理边界
20     memset(f, -0x3f, sizeof(f));
21     // 这里处理f(i, 1)的情况,也可以直接f(0, 0) = 0
22     for (int i = 1; i <= k; i++) {
23         f[i][1] = a[i];
24     }
25     
26     for (int i = 1; i <= n; i++) {
27         for (int j = 1; j <= m; j++) {
28             for (int u = max(1, i - k); u < i; u++) {
29                 f[i][j] = max(f[i][j], f[u][j - 1] + a[i]);
30             }
31         }
32     }
33     
34     LL ret = -1;
35     for (int i = n; i > n - k; i--) {
36         ret = max(ret, f[i][m]);
37     }
38     printf("%lld", ret);
39     
40     return 0;
41 }
复制代码

  在进行状态计算的时候,可以发现对于固定的j,每次都是从一个长度为k的窗口中求一个最大值,即i枚举的范围是iki1。因此可以用单调队列进行优化,先枚举j,再枚举i,每次维护长度为k的窗口中的最大值。

  时间复杂度为O(n2),AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 const int N = 210;
 9 
10 LL a[N], f[N][N];
11 LL q[N], hh, tt = -1;
12 
13 int main() {
14     int n, k, m;
15     scanf("%d %d %d", &n, &k, &m);
16     for (int i = 1; i <= n; i++) {
17         scanf("%d", a + i);
18     }
19     
20     memset(f, -0x3f, sizeof(f));
21     for (int i = 1; i <= k; i++) {
22         f[i][1] = a[i];
23     }
24     
25     for (int j = 2; j <= m; j++) {
26         hh = 0, tt = -1;
27         for (int i = 1; i <= n; i++) {
28             if (hh <= tt && q[hh] < i - k) hh++;
29             if (hh <= tt) f[i][j] = f[q[hh]][j - 1] + a[i];
30             while (hh <= tt && f[q[tt]][j - 1] <= f[i][j - 1]) {
31                 tt--;
32             }
33             q[++tt] = i;
34         }
35     }
36     
37     LL ret = -1;
38     for (int i = n; i > n - k; i--) {
39         ret = max(ret, f[i][m]);
40     }
41     printf("%lld", ret);
42     
43     return 0;
44 }
复制代码

 

参考资料

  AcWing 4418. 选元素(AcWing杯 - 周赛):https://www.acwing.com/video/3859/

posted @   onlyblues  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示