题解:洛谷 P9941 Even More Odd Photos

【题目来源】

洛谷:P9941 [USACO21JAN] Even More Odd Photos B - 洛谷

【题目描述】

Farmer John 正再一次尝试给他的 \(N\) 头奶牛拍照\((2\le N\le 1000)\)

每头奶牛有一个范围在 $1\dots 100 $之内的整数的「品种编号」。Farmer John 对他的照片有一个十分古怪的构思:他希望将所有的奶牛分为不相交的若干组(换句话说,将每头奶牛分到恰好一组中)并将这些组排成一行,使得第一组的奶牛的品种编号之和为偶数,第二组的编号之和为奇数,以此类推,奇偶交替。

Farmer John 可以分成的最大组数是多少?

【输入】

输入的第一行包含 \(N\)。下一行包含 \(N\) 个空格分隔的整数,为 \(N\) 头奶牛的品种编号。

【输出】

输出 Farmer John 的照片中的最大组数。可以证明,至少存在一种符合要求的分组方案。

【输入样例】

7
1 3 5 7 9 11 13

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(N\) 个整数,每个整数有奇偶性(奇数为1,偶数为0)。需要将所有数分成若干组,每组是一个非空集合(将每个数分到恰好一组),并将这些组排成一行,使得各组数字之和的奇偶性依次交替为:偶、奇、偶、奇……(即从偶数开始,奇偶交替)。求最大可能的分组数。本质是利用奇偶性特性进行贪心配对,与数值大小无关。

  2. 算法选择

    • 贪心 + 奇偶性调整:核心思想是最大化组数,因此应尽量让每组只包含一个数(单个奇数或偶数即可满足奇偶性要求),但当奇数和偶数的数量不满足交替序列的条件时,需要进行合并调整。
    • 奇偶性合成规则:偶数 + 偶数 = 偶数,奇数 + 奇数 = 偶数,偶数 + 奇数 = 奇数。因此:
      • 单个偶数可单独成组(组和为偶数)。
      • 单个奇数可单独成组(组和为奇数)。
      • 两个奇数可以合并成一个偶数(相当于将一个“奇数名额”转换为一个“偶数名额”)。
  3. 关键步骤(基于代码逻辑):

    • 统计:遍历所有数,统计奇数个数 odd 和偶数个数 even
    • 调整(核心)
      • 处理奇数过多:当 odd > even 时,交替序列(偶、奇、偶、奇…)需要偶数和奇数的数量要么相等(总数为偶数时),要么偶数比奇数多1(总数为奇数时)。为了让分组数最大,每对多余的奇数可以合并成一个偶数(两个奇数和为偶数),操作:odd -= 2even += 1。循环直到 odd <= even
      • 处理偶数过多:当 even > odd + 1 时,表明偶数太多了。为了最大化组数,需要将多余的偶数丢弃(即合并到其他组中,但不增加组数),使得 even = odd + 1。因为序列以偶数开头并以偶数结尾(如果总数为奇数)或奇偶数量相等(如果总数为偶数)时,偶数的最大数量只能是 odd + 1
    • 输出答案:最终总组数 = odd + even
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\),仅需一次遍历统计奇偶个数,调整操作为常数次循环(每次减少2个奇数,最多循环 \(O(N)\) 次,但总体仍为 \(O(N)\))。
    • 空间复杂度:\(O(1)\),仅需两个计数器。
  5. 贪心 + 奇偶性分析的核心思想

    • 问题简化:将“组和奇偶性”转化为“组内奇数的奇偶性”。一组数字之和的奇偶性仅由其中奇数的个数决定(奇数个奇数和为奇,偶数个奇数和为偶)。
    • 最大化组数的贪心策略:每个数单独成组时组数最多,但必须满足奇偶交替的约束。因此,只有在奇偶数量不匹配时,才被迫合并一些数。合并两个奇数(变成一个偶数)是唯一能调整奇偶数比例且不改变总和奇偶性的有效操作。
    • 边界条件:序列以偶数开头,因此偶数的数量不能小于奇数的数量,且最多只能比奇数多1。这个约束是贪心调整的终止条件。
    • 适用场景:适用于仅涉及整数奇偶性、需要构造最长的“奇偶交替”序列的计数问题。核心是通过数学性质(奇+奇=偶)进行数量匹配。

【算法标签】

普及- #数学

【代码详解】

#include <iostream>
using namespace std;
int n, odd, even;
int main()
{
    cin >> n;  // 输入n
    for (int i=1; i<=n; i++) {  // 遍历n个数
        int x;
        cin >> x;  // 输入数x
        if (x%2==1) odd++;  // 判断奇偶性,并统计奇数和偶数的个数
        else even++;
    }
    while (even<odd) {  // 当奇数个数大于偶数个数时
        odd -=2 ;  // 将奇数变为偶数,变一次奇数个数需要减2
        even++;  // 偶数个数自增1
    }
    if (even>odd+1) {  // 如果偶数大于奇数+1
        even = odd + 1;  // 则偶数等于奇数+1,如8个偶、1个奇,最后会变成2个偶、1个奇(1个偶数和1个奇数组成1个奇数,奇数个数没有变化,偶数在递减,直至奇数个数+1)
    }
    cout << odd + even <<endl;  // 最后输出奇数和偶数个数之和
    return 0;
}

【运行结果】

7
1 3 5 7 9 11 13
3
posted @ 2026-08-29 09:41  团爸讲算法  阅读(5)  评论(0)    收藏  举报