您的位置:首页 > 其它

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

2017-11-16 21:52 316 查看

题目

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

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=∑aiRi,i=0→6其中ai=(num−∑ajRj)/Ri,j=0→i−1上式的除法为整数整除。这里的分解是从大进制到小进制分解。一般情况下,按第1,2条规则,ai为多少,对应的罗马字符就有多少个;但由于第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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: