题解:洛谷 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

【核心思想】

  1. 问题分析:给定整数 \(n\),要求统计 \(1 \sim n\) 所有数字的罗马数字表示中,各字符(I, V, X, L, C, D, M)出现的总次数,并按罗马数字表递增顺序输出出现次数非零的字符。

  2. 算法选择

    • 数位拆分 + 查表转换:将每个数字按千、百、十、个位拆分,每个数位独立查表转换为罗马数字子串
    • 字符串拼接统计:将所有罗马数字拼接成一个大字符串,用桶数组统计各字符出现次数
  3. 关键步骤

    • 读入\(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, V, X, L, C, D, M 顺序输出次数非零的字符及其次数
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),每个数字转换为 \(O(1)\),总字符串长度为 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储拼接后的总字符串
  5. 查表转换的核心思想

    • 数位独立性:罗马数字的各位(千、百、十、个)表示相互独立,可分别查表后拼接
    • 减法表示法:如 \(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
posted @ 2026-08-03 08:47  团爸讲算法  阅读(9)  评论(0)    收藏  举报