子序列

子序列

给定一个长度为 $n$ 的整数序列 $a_{1},a_{2}, \dots,a_{n}$。

请你找到一个该序列的子序列,要求:

  1. 该子序列的所有元素之和必须是奇数。
  2. 在满足条件 $1$ 的前提下,该子序列的所有元素之和应尽可能大。

输出你找到的满足条件的子序列的所有元素之和。

保证至少存在一个满足条件的子序列。

注意,子序列不一定连续。

输入格式

第一行包含一个整数 $n$。

第二行包含 $n$ 个整数 $a_{1},a_{2}, \dots,a_{n}$。

输出格式

输出一个整数,表示满足条件的子序列的所有元素之和。

数据范围

前 $6$ 个测试点满足 $1 \leq n \leq 5$。
所有测试点满足 $1 \leq n \leq {10}^{5}$,${−10}^{4} \leq a_{i} \leq {10}^{4}$。

输入样例1:

4
-2 2 -3 1

输出样例1:

3

输入样例2:

3
2 -5 -3

输出样例2:

-1

 

解题思路

  把所有的正数相加,然后分类讨论:

  • 所有的正数的和为奇数,那么这个就是最大值。
  • 所有的正数的和为偶数:
  1. 负偶数一定不选,因为一个数加上偶数不影响奇偶性,而且加上负偶数总和会变小。
  2. 正偶数一定选,同理因为一个数加上偶数不影响奇偶性,而且加上正偶数总和会变大。
  3. 负奇数最多选一个,如果多于一个,我们总是可以选出两个负奇数,这两个负奇数的和就变成负偶数了,加上后不影响奇偶性且总和变小。
  4. 正奇数最多只能有一个不选,如多于一个不选,那么总是可以选出两个正奇数,这两个正奇数的和是正偶数,加上后不影响奇偶性且总和会变大。

  因此贪心的思路是,先把所有的正数加起来,如果得到的总和是奇数,那么就是答案;如果是偶数,那么就需要加上一个负奇数或者减去一个正奇数,答案就是这两种情况的一个最大值。

  AC代码如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 int main() {
 6     int n, a = -2e9, b = 2e9, sum = 0;
 7     scanf("%d", &n);
 8     while (n--) {
 9         int val;
10         scanf("%d", &val);
11         if (val > 0) sum += val;
12         if (val < 0 && val % 2) a = max(a, val);
13         else if (val > 0 && val % 2) b = min(b, val);
14     }
15     
16     printf("%d", sum % 2 ? sum : max(sum + a, sum - b));
17     
18     return 0;
19 }

  这题还可以用动态规划来做,因为可以发现这是一个背包(组合)的问题。

  设集合$f \left( {i, j} \right)$表示从前$i$个数中选,且选的数的总和模$2$后为$j$的所有方案的集合。$j \in \left\{ {0, 1} \right\}$。

  状态转移方程为$f \left( {i, j} \right) = max \left\{ {f(i-1, j),~ f(i-1, (j-a_{i}) \% 2)} \right\}$。

  AC代码如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 1e5 + 10;
 7 
 8 int a[N], f[N][2];
 9 
10 int main() {
11     int n;
12     scanf("%d", &n);
13     for (int i = 1; i <= n; i++) {
14         scanf("%d", a + i);
15     }
16     
17     memset(f, -0x3f, sizeof(f));
18     f[0][0] = 0;
19     for (int i = 1; i <= n; i++) {
20         for (int j = 0; j <= 1; j++) {
21             f[i][j] = max(f[i - 1][j], f[i - 1][((j - a[i]) % 2 + 2) % 2] + a[i]);
22         }
23     }
24     
25     printf("%d", f[n][1]);
26     
27     return 0;
28 }

 

参考资料

  AcWing 4414. 子序列(AcWing杯 - 周赛):https://www.acwing.com/video/3846/

posted @ 2022-05-09 10:49  onlyblues  阅读(72)  评论(0编辑  收藏  举报
Web Analytics