题解:洛谷 P14357 拼数
【题目来源】
洛谷:P14357 [CSP-J 2025] 拼数 / number(民间数据) - 洛谷
【题目描述】
小 R 正在学习字符串处理。小 X 给了小 R 一个字符串 \(s\),其中 \(s\) 仅包含小写英文字母及数字,且包含至少一个 \(1 \sim 9\) 中的数字。小 X 希望小 R 使用 \(s\) 中的任意多个数字,按任意顺序拼成一个正整数。注意:小 R 可以选择 \(s\) 中相同的数字,但每个数字只能使用一次。例如,若 \(s\) 为 \(\tt 1a01b\),则小 R 可以同时选择第 \(1,3,4\) 个字符,分别为 \(1,0,1\),拼成正整数 \(101\) 或 \(110\);但小 R 不能拼成正整数 \(111\),因为 \(s\) 仅包含两个数字 \(1\)。小 R 想知道,在他所有能拼成的正整数中,最大的是多少。你需要帮助小 R 求出他能拼成的正整数的最大值。
【输入】
输入的第一行包含一个字符串 \(s\),表示小 X 给小 R 的字符串。
【输出】
输出一行一个正整数,表示小 R 能拼成的正整数的最大值。
【输入样例】
5
【输出样例】
5
【核心思想】
-
问题分析:给定一个包含小写字母和数字的字符串 \(s\),要求从中选取任意多个数字(每个数字字符只能用一次),按任意顺序拼接成一个最大的正整数。本质上是贪心排序问题:要使拼接后的整数最大,只需将所有可用数字按降序排列输出。
-
算法选择:
- 计数排序:统计字符串中每个数字 \(0 \sim 9\) 的出现次数
- 降序输出:从 \(9\) 到 \(0\) 依次输出每个数字的所有出现
-
关键步骤:
- 读取数据:读入字符串 \(s\)
- 统计数字频次:遍历 \(s\),对每个数字字符 \(c\),\(B[c - '0'] \leftarrow B[c - '0'] + 1\)
- 降序输出(\(i = 9\) 到 \(0\)):
- 若 \(B[i] > 0\),循环输出 \(i\) 共 \(B[i]\) 次
- 输出换行
-
时间/空间复杂度:
- 时间复杂度:\(O(|s|)\),线性扫描字符串
- 空间复杂度:\(O(1)\),固定大小的计数数组
-
贪心降序的核心思想:
- 字典序最大化:多位整数的比较从最高位开始,因此将大的数字放在前面总是更优
- 计数排序的简洁性:数字只有 \(10\) 种,用计数数组 \(O(|s|)\) 完成排序,无需通用排序算法
- 字母的自动过滤:非数字字符在
s[i] - '0'时会产生非法索引,但代码中未显式过滤;实际上输入保证至少有一个数字,且字母的 ASCII 值与数字相差较大,在合理范围内不会冲突(但严格来说应加isdigit判断) - 正整数的隐含约束:输出至少包含一个数字,且前导零问题不存在(因为是从 \(9\) 到 \(0\) 输出,\(0\) 总是在最后)
- 适用于数字提取、贪心排序、简单构造类问题
【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
string s; // 存储输入的数字字符串
int B[15]; // 计数数组,记录每个数字(0-9)出现的次数
int main()
{
// 输入数字字符串
cin >> s;
// 统计每个数字出现的次数
for (int i = 0; i < s.size(); i++)
{
// 将字符转换为数字索引,并增加对应计数
B[s[i] - '0']++;
}
// 从大到小输出数字(9到0)
for (int i = 9; i >= 0; i--)
{
// 如果当前数字出现过
if (B[i] != 0)
{
// 输出该数字的所有出现次数
while (B[i] > 0)
{
cout << i; // 输出当前数字
B[i]--; // 减少计数
}
}
}
// 输出换行符
cout << endl;
return 0;
}
【运行结果】
5
5
【算法真传】
浙公网安备 33010602011771号