题解:学而思编程 最大整数(一)

【题目来源】

学而思编程:最大整数(一)

【题目描述】

小猴最近学习数位拆分,猴博士为了考验小猴的学习成果,特意给小猴一道简单的题目:

给定小猴一个正整数 \(n\),要求小猴只能交换该整数奇偶性相同的任意两个数字,不限制交换次数,最终能得到的最大整数是多少?

例如,\(n=1234\),可以交换数字 \(3\) 和数字 \(1\),并交换数字 \(2\) 和数字 \(4\) 之后可以使得得到的整数最大位 \(3412\)

请你帮助小猴完成这个题目。

【输入】

一行一个正整数 \(n\)

【输出】

一行一个整数,表示能得到的最大整数。

【输入样例】

5678

【输出样例】

7856

【核心思想】

  1. 问题分析:给定正整数 \(n\),只能交换奇偶性相同的数字(即偶数数字之间可任意交换,奇数数字之间可任意交换),求能得到的最大整数。这是一个分组排序 + 位置映射问题,关键在于识别奇偶数字的交换独立性,将问题分解为两个独立的排序子问题。

  2. 算法选择

    • 奇偶分离:将数字按奇偶性分成两组
    • 组内降序排序:每组内部按从大到小排序
    • 按原位置回填:根据原数字的奇偶性,从排序后的对应组中依次取最大数字填入
  3. 关键步骤

    • 初始化:读取数字字符串 \(s[1..n]\)
    • 奇偶分离
      • 遍历 \(i\)\(1\)\(n\)
        • \((s_i - '0') \bmod 2 = 0\):将 \(s_i\) 加入偶数数组 even
        • 否则:将 \(s_i\) 加入奇数数组 odd
    • 组内降序排序
      • sort(even + 1, even + cur_even + 1, cmp):偶数从大到小
      • sort(odd + 1, odd + cur_odd + 1, cmp):奇数从大到小
    • 按位置回填
      • 初始化指针 \(e = 0\)(偶数数组指针),\(o = 0\)(奇数数组指针)
      • 遍历 \(i\)\(1\)\(n\)
        • 若原位置 \(i\) 是偶数:s[i] = even[++e](填入当前最大的未使用偶数)
        • 否则:s[i] = odd[++o](填入当前最大的未使用奇数)
    • 输出结果字符串
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log n)\),分离 \(O(n)\),排序 \(O(n \log n)\),回填 \(O(n)\)
    • 空间复杂度:\(O(n)\),偶数数组和奇数数组
  5. 分组排序的核心思想

    • 交换独立性:偶数数字只能与偶数数字交换位置,奇数数字只能与奇数数字交换位置,两组完全独立,不存在跨组干扰
    • 贪心最优性:在每个奇偶组内,要使最终整数最大,应将较大的数字放在更靠前的位置。由于同组数字可任意交换,组内完全排序即可达到最优
    • 位置保持策略:原数字的奇偶性决定了该位置"属于哪个组",排序后按原位置顺序回填,保证高位(靠前位置)获得组内最大可用数字
    • 字典序最大化:多位整数的比较从最高位开始,该策略确保每个位置在所属组的约束下取到可能的最大值,从而实现全局最大
    • 适用于"分组独立交换 + 字典序最大化"类问题,分离排序后回填是通用解法

【算法标签】

其他排序

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
char s[N], even[N], odd[N];
int cur_even, cur_odd;  // 记录偶数数字和奇数数字的数量

bool cmp(char x, char y)
{
    return x > y;  // 从大到小排序
}

int main()
{
    cin >> s + 1;  // 输入字符串,从索引1开始
    int n = strlen(s + 1);  // 字符串长度

    // 分离偶数和奇数数字
    for (int i = 1; i <= n; i++)
    {
        if ((s[i] - '0') % 2 == 0)  // 判断是否为偶数
        {
            even[++cur_even] = s[i];
        }
        else  // 奇数
        {
            odd[++cur_odd] = s[i];
        }
    }

    // 分别对偶数和奇数数字排序
    sort(even + 1, even + cur_even + 1, cmp);  // 偶数从大到小
    sort(odd + 1, odd + cur_odd + 1, cmp);  // 奇数从大到小

    // 按照原始位置重新填入排序后的数字
    int e = 0, o = 0;  // 分别指向偶数数组和奇数数组的当前位置
    for (int i = 1; i <= n; i++)
    {
        if ((s[i] - '0') % 2 == 0)  // 原位置是偶数
        {
            s[i] = even[++e];  // 填入排序后的偶数
        }
        else  // 原位置是奇数
        {
            s[i] = odd[++o];  // 填入排序后的奇数
        }
    }

    cout << s + 1 << endl;  // 输出结果,s+1表示从索引1开始
    return 0;
}

【运行结果】

5678
7856
posted @ 2026-07-04 14:39  团爸讲算法  阅读(7)  评论(0)    收藏  举报