题目
题目链接:整数转换成罗马数字 - 题库 - 计蒜课
1000ms,65536K
给定一个整数 num,将整数转换成罗马数字。
如 1, 2, 3, 4, 5 对应的罗马数字分别为I,II,III,IV,V等,更详细的说明见此 链接。输入格式
第一行输入一个整数 num(1≤num≤3999)。
输出格式
输出 num 对应的罗马数字。
样例输入
123
样例输出
CXXIII
分析
知识准备
M D C L X V I 1000 500 100 50 10 5 1 1. 相同的数字连写、所表示的数等于这些数字相加得到的数、如:Ⅲ=3;
2. 小的数字在大的数字的右边、所表示的数等于这些数字相加得到的数、 如:Ⅷ=8、Ⅻ=12;
3. 小的数字(限于 I、X 和 C)在大的数字的左边、所表示的数等于大数减小数得到的数、如:Ⅳ=4、Ⅸ=9;
4. 正常使用时、连写的数字重复不得超过三次
基本思路
要将一个整数num(1≤num≤3999)转成罗马数字,首先我们可以对它进行分解:
进制数 | M | D | C | L | X | V | I |
---|---|---|---|---|---|---|---|
数值 | 1000 | 500 | 100 | 50 | 10 | 5 | 1 |
数值表示 | R0 | R1 | R2 | R3 | R4 | R5 | R6 |
个数 | a0 | a1 | a2 | a3 | a4 | a5 | a6 |
观察发现
根据上面的分解公式可知:
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;
}