题解:学而思编程 更奇怪的照片
【题目来源】
学而思编程:更奇怪的照片
【题目描述】
Farmer John 正再一次尝试给他的 \(N\) 头奶牛拍照(\(2≤N≤1000\))。
每头奶牛有一个范围在 \(1…100\) 之内的整数的「品种编号」。Farmer John 对他的照片有一个十分古怪的构思:他希望将所有的奶牛分为不相交的若干组(换句话说,将每头奶牛分到恰好一组中)并将这些组排成一行,使得第一组的奶牛的品种编号之和为偶数,第二组的编号之和为奇数,以此类推,奇偶交替。
Farmer John 可以分成的最大组数是多少?
【输入】
输入的第一行包含 \(N\)。下一行包含 \(N\) 个空格分隔的整数,为 \(N\) 头奶牛的品种编号。
【输出】
输出 Farmer John 的照片中的最大组数。可以证明,至少存在一种符合要求的分组方案。
【输入样例】
7
1 3 5 7 9 11 13
【输出样例】
3
【核心思想】
-
问题分析:给定 \(N\) 个奶牛的品种编号,要求分成尽可能多的组,按顺序排列使得各组和的奇偶性交替(偶、奇、偶、奇...)。这是一个奇偶分析 + 贪心调整问题,关键在于统计奇数和偶数的数量,并通过合并调整使它们能形成最长交替序列。
-
算法选择:
- 奇偶计数:统计奇数个数
odd和偶数个数even - 贪心合并:当奇数过多时,将两个奇数合并为一个偶数(奇+奇=偶),使奇偶数量趋于平衡
- 边界调整:最终偶数最多比奇数多 \(1\)(序列以偶数开头),否则需要截断
- 奇偶计数:统计奇数个数
-
关键步骤:
- 初始化:读取 \(N\) 和 \(N\) 个品种编号
- 统计奇偶数量:
- 遍历每个数 \(t\):
- 若 \(t \bmod 2 = 0\):
even++ - 否则:
odd++
- 若 \(t \bmod 2 = 0\):
- 遍历每个数 \(t\):
- 调整奇偶平衡:
- 当
odd > even时循环:odd -= 2:两个奇数合并为一个偶数even++:合并后得到一个偶数
- 当
- 最终边界处理:
- 若
even > odd:even = odd + 1:偶数最多比奇数多 \(1\)(序列以偶开头)
- 若
- 输出
even + odd:最大组数
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),线性遍历统计,调整循环最多 \(O(N)\)
- 空间复杂度:\(O(1)\),仅需计数变量
-
奇偶合并的核心思想:
- 交替序列结构:目标序列形如 偶、奇、偶、奇... 或 偶、奇、偶(以偶开头),因此偶数个数等于奇数个数或比奇数多 \(1\)
- 奇数合并策略:两个奇数之和为偶数,因此当奇数过多时,可将两个奇数合并为一组(和为偶),相当于消耗 \(2\) 个奇数产生 \(1\) 个偶数,使奇偶比例趋于 \(1:1\)
- 贪心最优性:每次合并减少 \(2\) 个奇数增加 \(1\) 个偶数,是使奇偶数量接近的最快方式,局部最优即全局最优
- 边界截断:若偶数仍多于奇数 \(+1\),说明偶数过剩,只能保留
odd + 1个偶数以维持交替结构 - 适用于"奇偶交替分组 + 资源调整"类问题,通过合并操作将不平衡状态转化为可交替结构
【算法标签】
数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, odd, even; // n: 数字个数,odd: 奇数个数,even: 偶数个数
int main()
{
cin >> n; // 读入数字个数
for (int i = 1; i <= n; i++) // 遍历每个数字
{
int t;
cin >> t; // 读入当前数字
if (t % 2 == 0) // 如果是偶数
{
even++; // 偶数计数加1
}
else // 如果是奇数
{
odd++; // 奇数计数加1
}
}
// 调整奇偶数量,使它们可以交替排列
while (odd > even) // 当奇数多于偶数时
{
odd -= 2; // 将两个奇数合并成一个偶数
even++; // 合并后得到一个偶数
}
if (even > odd) // 如果偶数多于奇数
{
even = odd + 1; // 最多只能多一个偶数
}
// 输出最大交替序列长度
cout << even + odd << endl; // 总长度是奇数和偶数个数之和
return 0;
}
【运行结果】
7
1 3 5 7 9 11 13
3
浙公网安备 33010602011771号