题解:学而思编程 最大整数(一)
【题目来源】
学而思编程:最大整数(一)
【题目描述】
小猴最近学习数位拆分,猴博士为了考验小猴的学习成果,特意给小猴一道简单的题目:
给定小猴一个正整数 \(n\),要求小猴只能交换该整数奇偶性相同的任意两个数字,不限制交换次数,最终能得到的最大整数是多少?
例如,\(n=1234\),可以交换数字 \(3\) 和数字 \(1\),并交换数字 \(2\) 和数字 \(4\) 之后可以使得得到的整数最大位 \(3412\)。
请你帮助小猴完成这个题目。
【输入】
一行一个正整数 \(n\)。
【输出】
一行一个整数,表示能得到的最大整数。
【输入样例】
5678
【输出样例】
7856
【核心思想】
-
问题分析:给定正整数 \(n\),只能交换奇偶性相同的数字(即偶数数字之间可任意交换,奇数数字之间可任意交换),求能得到的最大整数。这是一个分组排序 + 位置映射问题,关键在于识别奇偶数字的交换独立性,将问题分解为两个独立的排序子问题。
-
算法选择:
- 奇偶分离:将数字按奇偶性分成两组
- 组内降序排序:每组内部按从大到小排序
- 按原位置回填:根据原数字的奇偶性,从排序后的对应组中依次取最大数字填入
-
关键步骤:
- 初始化:读取数字字符串 \(s[1..n]\)
- 奇偶分离:
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 若 \((s_i - '0') \bmod 2 = 0\):将 \(s_i\) 加入偶数数组
even - 否则:将 \(s_i\) 加入奇数数组
odd
- 若 \((s_i - '0') \bmod 2 = 0\):将 \(s_i\) 加入偶数数组
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 组内降序排序:
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](填入当前最大的未使用奇数)
- 若原位置 \(i\) 是偶数:
- 输出结果字符串
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),分离 \(O(n)\),排序 \(O(n \log n)\),回填 \(O(n)\)
- 空间复杂度:\(O(n)\),偶数数组和奇数数组
-
分组排序的核心思想:
- 交换独立性:偶数数字只能与偶数数字交换位置,奇数数字只能与奇数数字交换位置,两组完全独立,不存在跨组干扰
- 贪心最优性:在每个奇偶组内,要使最终整数最大,应将较大的数字放在更靠前的位置。由于同组数字可任意交换,组内完全排序即可达到最优
- 位置保持策略:原数字的奇偶性决定了该位置"属于哪个组",排序后按原位置顺序回填,保证高位(靠前位置)获得组内最大可用数字
- 字典序最大化:多位整数的比较从最高位开始,该策略确保每个位置在所属组的约束下取到可能的最大值,从而实现全局最大
- 适用于"分组独立交换 + 字典序最大化"类问题,分离排序后回填是通用解法
【算法标签】
其他排序
【代码详解】
#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
浙公网安备 33010602011771号