计蒜客CSP-J2模拟赛:奇数

2023年10月6日 | 分类: 【编程】

【题面】

题目描述

给你一个长度为 \(n\) 的序列 \(a\) ,你要从序列中任选若干个数字求和(至少一个),求最终能组成的最大奇数为多少,无法组成输出 \(-1\) 。

输入格式

输入的第一行包含一个正整数 \(n\) 。

第二行一共包含 \(n\) 个整数,分别表示 \(a_1,a_2,\dots,a_n\) 。

输出格式

输出一行一个整数,表示最大奇数的值,无解输出 \(-1\) 。

数据范围

对于 100% 的数据, \(1≤n≤20,{−10^6}≤{a_i}≤{10^6}\) 。

各测试点的附加限制如下表所示:

测试点编号 	n≤ 	特殊性质
1∼2 		1 	n=1
3∼5 		2 	n=2
6∼9 		10 	没有负数
10∼11 		20 	只有奇数个奇数
12∼13 		20 	只有偶数个奇数
14∼15 		20 	没有偶数
16∼17 		20 	没有奇数
18∼20 		20

格式说明

输出时每行末尾的多余空格,不影响答案正确性

输入、输出要求

要求使用「文件输入、输出」的方式解题,输入文件为 odd.in,输出文件为 odd.out

样例输入1

5
1 3 6 5 -2

样例输出1

15

样例解释1

选择 1,3,6,5 组成最大奇数,所以最大奇数为 1+3+6+5=15 ,可以证明没有更大的奇数。

样例输入2

4
2 4 6 8

样例输出2

-1

样例解释2

无法选出任何奇数。

【解析】

知识点:分类讨论、数学。

因为 \(n\) 只有 \(20\) ,相对简单:

1. 如果不想分类讨论,可以直接搜索枚举每个数选/不选,然后对答案为奇数的取 \(max\) ,复杂度 \(O{({2^n}{\cdot}{n})}\) 。

2. 如果考虑分类讨论:

如果没有奇数,肯定直接输出 \(-1\) 。

注意:因为本题 \(a_i\) 可能为负数,那么 \({a_i}%2\) 可能等于 \(-1\) ,所以判断奇数的时候应该使⽤ \({a_i}%2!=0\) 。

根据:
1. 偶数 + 偶数 = 偶数;
2. 奇数 + 奇数 = 偶数
3. 偶数 + 奇数 = 奇数;

如果答案加上⼀个偶数,并不会影响其奇偶性。所以对于所有⾮负偶数,直接累加,计⼊答案。

那么剩下的就是奇数的选择:
1. 因为最终答案要求的是最⼤奇数,所以要选择⾄少 1 个奇数,把最⼤的奇数计⼊答案。
2. 对于剩下的奇数,不能只选⼀个(会使得最终答案变为偶数),所以需要两个一组地选。那么思路就很明显了,如果你要使得答案增加,那就
要让这两个奇数加和为正。
3. 所以对剩下的奇数排序,每次选取最⼤的两个,如果这两个加和⾮负,那就把他们加⼊到答案中。

【参考代码】

sum是summation的缩写,意为求和
cnt是count的缩写,意为计数器
aver是average value,意为平均值

#include <bits/stdc++.h>
using namespace std;
int main() {
	freopen("odd.in", "r", stdin);
	freopen("odd.out", "w", stdout);
	int n;
	int a[25], b[25];
	int sum = 0, cnt = 0;

	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		if (a[i] % 2 != 0) { // 因为存在负数,所以不能用 == 1 进⾏判断
			b[cnt++] = a[i];
		}
		if (a[i] % 2 == 0 && a[i] > 0) {
			sum += a[i];
		}
	}
	if (cnt == 0) {
		cout << -1 << '\n';
	} else {
		sort(b, b + cnt, greater<int>());
		sum += b[0]; // 最少选⼀个奇数
		for (int i = 1; i + 1 < cnt; i += 2) {
			if (b[i] + b[i + 1] >= 0) {
				sum += (b[i] + b[i + 1]);
			}
		}
		cout << sum << '\n';
	}
	return 0;
}