题解:洛谷 P1465 序言页码
【题目来源】
洛谷:P1465 [USACO2.2] 序言页码 Preface Numbering - 洛谷
【题目描述】
给定 \(n\),求 \(1∼n\) 的 罗马数字 表示中,各个字符出现了多少次。
比如 \(n=5\),表示为 I, II, III, IV, V。总共有 \(7\) 个 I 出现,\(2\) 个 V 出现。
【输入】
一个整数 \(n\)。
【输出】
每行一个字符和一个数字 \(k\),表示这个字符出现了 \(k\) 次。字符必须按罗马数字表中的递增顺序输出。
【输入样例】
5
【输出样例】
I 7
V 2
【核心思想】
-
问题分析:给定整数 \(n\),要求统计 \(1 \sim n\) 所有数字的罗马数字表示中,各字符(
I, V, X, L, C, D, M)出现的总次数,并按罗马数字表递增顺序输出出现次数非零的字符。 -
算法选择:
- 数位拆分 + 查表转换:将每个数字按千、百、十、个位拆分,每个数位独立查表转换为罗马数字子串
- 字符串拼接统计:将所有罗马数字拼接成一个大字符串,用桶数组统计各字符出现次数
-
关键步骤:
- 读入:\(n\)
- 定义转换表:
- 个位
g[0..9]:"","I","II","III","IV","V","VI","VII","VIII","IX" - 十位
s[0..9]:"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" - 百位
b[0..9]:"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" - 千位
q[0..3]:"","M","MM","MMM"(因 \(n \le 3499\))
- 个位
- 转换函数
convert(x):- 拆分千位
qian = x/1000、百位bai = x/100%10、十位shi = x/10%10、个位ge = x%10 - 拼接
q[qian] + b[bai] + s[shi] + g[ge]返回罗马数字字符串
- 拆分千位
- 统计:
- 遍历 \(i\) 从 \(1\) 到 \(n\),将
convert(i)拼接到总字符串 \(s1\) - 遍历 \(s1\),用桶数组
a[ch]统计每个字符出现次数
- 遍历 \(i\) 从 \(1\) 到 \(n\),将
- 输出:按
I, V, X, L, C, D, M顺序输出次数非零的字符及其次数
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),每个数字转换为 \(O(1)\),总字符串长度为 \(O(n)\)
- 空间复杂度:\(O(n)\),存储拼接后的总字符串
-
查表转换的核心思想:
- 数位独立性:罗马数字的各位(千、百、十、个)表示相互独立,可分别查表后拼接
- 减法表示法:如 \(4 = \text{IV}\)、\(9 = \text{IX}\) 等特殊情况直接预存在表中,避免复杂逻辑判断
- 桶统计简化:将所有罗马数字拼接后统一统计,避免逐个数字维护计数器
- 顺序输出控制:按固定字符顺序
I, V, X, L, C, D, M判断输出,保证结果有序 - 适用于罗马数字转换、数位拆分查表、字符统计类问题
【算法标签】
普及 #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, a[26];
string s1;
string convert(int x)
{
string str="";
// 使用列表形式,记录下个十百千的字符(懒的算3的倍数的关系了)
string g[10]={"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
string s[10]={"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
string b[10]={"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
string q[4]={"", "M", "MM", "MMM"};
int qian = x/1000; // 数位拆分
int bai = x/100%10;
int shi = x/10%10;
int ge = x%10;
str += q[qian]; // 再进行字符串拼接
str += b[bai];
str += s[shi];
str += g[ge];
return str;
}
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // 遍历1~n的每个数字
s1 += convert(i); // 将其转换为字符串,并拼到s1中
}
for (int i=0; i<s1.length(); i++) { // 遍历s1字符串,使用桶排序,记录每个字符的次数
a[s1[i]]++;
}
if (a['I']!=0) cout << "I " << a['I'] << endl; // 按照罗马字符表的顺序输出
if (a['V']!=0) cout << "V " << a['V'] << endl;
if (a['X']!=0) cout << "X " << a['X'] << endl;
if (a['L']!=0) cout << "L " << a['L'] << endl;
if (a['C']!=0) cout << "C " << a['C'] << endl;
if (a['D']!=0) cout << "D " << a['D'] << endl;
if (a['M']!=0) cout << "M " << a['M'] << endl;
return 0;
}
【运行结果】
7
I 10
V 4
浙公网安备 33010602011771号