计蒜课:整数转换成罗马数字

题目

题目链接:整数转换成罗马数字 - 题库 - 计蒜课

1000ms,65536K

给定一个整数 num,将整数转换成罗马数字。
如 1, 2, 3, 4, 5 对应的罗马数字分别为I,II,III,IV,V等,更详细的说明见此 链接

输入格式

第一行输入一个整数 num(1≤num≤3999)。

输出格式

输出 num 对应的罗马数字。

样例输入

123

样例输出

CXXIII

分析

知识准备

罗马数字_百度百科#记数方法

MDCLXVI
1000500100501051

1. 相同的数字连写、所表示的数等于这些数字相加得到的数、如:Ⅲ=3;
2. 小的数字在大的数字的右边、所表示的数等于这些数字相加得到的数、 如:Ⅷ=8、Ⅻ=12;
3. 小的数字(限于 I、X 和 C)在大的数字的左边、所表示的数等于大数减小数得到的数、如:Ⅳ=4、Ⅸ=9;
4. 正常使用时、连写的数字重复不得超过三次

基本思路

要将一个整数num(1≤num≤3999)转成罗马数字,首先我们可以对它进行分解:

进制数MDCLXVI
数值1000500100501051
数值表示R0R1R2R3R4R5R6
个数a0a1a2a3a4a5a6

num=aiRii=06
其中
ai=(numajRj)/Rij=0i1
上式的除法为整数整除。 这里的分解是从大进制到小进制分解。一般情况下,按第1,2条规则,a i为多少,对应的罗马字符就有多少个;但由于第3,4条规则的限制,所以我们还需要 对ai进行修正以及 进制数位置进行修正

观察发现

根据上面的分解公式可知:
1. 对于进制为D(500), L(50), V(5)来说,0≤ai≤1,因为如果超过1,就会向前进位;
对于M(1000), C(100), X(10), I(1)l来说,0≤ai≤4,因为如果超过4,就会向前进位;
2. 当一个数某部分需要用到进制相减来表示时,可以相减的两个进制数距离不会超过2个进制距离。否则,这个数一定可以分成出中间的进制,可以用枚举去简单验证;
3. 被减的进制数一定是I(1), X(10), C(100)

特殊情况处理

特殊情况主要发生在要进行进制相减导致进制数位置的变化,有两类这样的问题:
1. 相邻两个进制相减,比如 4:IV,允许我记为“4”问题
2. 相隔1个进制相减,比如 9:IX,记为“9”问题,这种情况相减的时候需要向前借位,高位需要减1。

有更好的算法,欢迎留言联系^_^

实现

我是用c语言实现的(事实上文件保存为c++)

#include <stdio.h>
int main(int argc, char const *argv[]) {
    char RomanSym[7] = {'M', 'D', 'C', 'L', 'X', 'V', 'I'};
    int  RomanNum[7] = {1000, 500, 100, 50, 10, 5, 1};
    int  a = 0;
    int  k[7] = {0};
    char output[100];  // 将翻译成的罗马数字字符串输出到output
    scanf("%d", &a);
    for (int i = 0; i < 7; i++) {
        k[i] = a / RomanNum[i];
        a -= k[i] * RomanNum[i];
    }
    a = 0;  // 重新利用a,作为数组下标
    for (int i = 6; i >= 0; i--) {
        // 逆序
        if (k[i] == 4 && k[i-1] == 0) {
            // "4"问题
            output[a++] = RomanSym[i-1];
            output[a++] = RomanSym[i];
        } else if (k[i] == 4 && k[i-1] == 1) {
            // "9"问题
            k[i-1]--; // 向前借位需要减1
            output[a++] = RomanSym[i-2];
            output[a++] = RomanSym[i];
        } else {
            // 其他情况循环输出即可
            for (int j = 0; j < k[i]; j++) {
                output[a++] = RomanSym[i];
            }
        }
    }
    // 逆序输出
    for (int i = a - 1; i >= 0; i--) {
        putchar(output[i]);
    }
    putchar('\n');
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值