# 12. Integer to Roman
**練習日期:** 2026-02-09
**難度:** Medium
**類型:** Math、String
## 📘 題目敘述
給你一個整數 `num`,請把它轉換成羅馬數字並回傳。
羅馬數字符號與數值如下:
* `I` = 1
* `V` = 5
* `X` = 10
* `L` = 50
* `C` = 100
* `D` = 500
* `M` = 1000
羅馬數字通常由大到小排列,並且用下列特殊規則表示減法:
* `I` 可以放在 `V`(5)和 `X`(10)前面,形成 `IV`(4)和 `IX`(9)
* `X` 可以放在 `L`(50)和 `C`(100)前面,形成 `XL`(40)和 `XC`(90)
* `C` 可以放在 `D`(500)和 `M`(1000)前面,形成 `CD`(400)和 `CM`(900)
題目保證輸入 `num` 會在 `1` 到 `3999` 的範圍內。
### 條件限制
* `1 <= num <= 3999`
## 🧠 解題思路(一)if else判斷
我的想法就是**把數字拆成每一位(千、百、十、個),然後把每一位所有可能情況都判斷完**。
因為羅馬數字的規則其實非常固定,每一位都只會遇到幾種形式:
* 0:不加任何字
* 1~3:重複加 1 的符號(例如百位 `C`、十位 `X`、個位 `I`)
* 4:用減法寫成 `1` 在 `5` 前面(例如 `IV`、`XL`、`CD`)
* 5:直接加 `5` 的符號(例如 `V`、`L`、`D`)
* 6~8:先加 `5` 的符號,再補 1 的符號(例如 `VI`、`LXX`、`DCCC`)
* 9:用減法寫成 `1` 在 `10` 前面(例如 `IX`、`XC`、`CM`)
所以我就按照「千位 → 百位 → 十位 → 個位」的順序,一段一段把字串加上去,最後得到完整羅馬數字。
### 所有變數
* `num`:題目輸入的整數(我會一路用 `%=` 把已處理的高位扣掉)
* `s`:最後要回傳的羅馬數字字串(一邊處理一邊往後加)
* `digit`:目前處理那一位的數字(千位、百位、十位時用它)
## 🪜 主要流程步驟
### 1. 先處理千位:只會出現 M 的重複
我先算 `digit = num / 1000`,代表千位是幾。
千位的羅馬數字只有一種形式:重複 `M`,所以直接加 `digit` 次 `M`。
處理完後我用 `num %= 1000` 把千位去掉,準備處理百位。
---
### 2. 再處理百位:把 4、9、5~8 這些特殊情況先判斷掉
百位用 `digit = num / 100`。
我先處理最特殊的兩種減法:
* `digit == 4` → 加 `"CD"`
* `digit == 9` → 加 `"CM"`
如果不是 4 或 9,接著處理大於等於 5 的情況:
* `digit > 4` → 先加 `'D'`,並讓 `digit -= 5`
剩下的 `digit` 就只會是 `0~3`,我再補上 `digit` 次 `'C'`。
最後 `num %= 100`,進入十位。
---
### 3. 十位的寫法完全同百位,只是符號換成 X / L / C
十位用 `digit = num / 10`。
同樣先判斷:
* `digit == 4` → `"XL"`
* `digit == 9` → `"XC"`
* `digit > 4` → 先加 `'L'` 並 `digit -= 5`
* 補上 `digit` 次 `'X'`
最後 `num %= 10`,剩下就是個位。
---
### 4. 個位同樣處理 4、9、5~8,再補 I
個位直接用剩下的 `num`:
* `num == 4` → `"IV"`
* `num == 9` → `"IX"`
* `num > 4` → 先加 `'V'` 並 `num -= 5`
* 最後補上 `num` 次 `'I'`
---
### 5. 回傳答案字串
所有位數處理完後,回傳 `s`。
---
### 這個方法的優缺點
這個方法用了一堆if else的條件判斷數字大小條件,但如果位數一多其實就會很麻煩,所以還有更好的方式來做。
但也因為只有用條件判斷,其實速度很快而且記憶體大小也不大。
## 💻 程式碼實作(一)
```cpp
class Solution {
public:
string intToRoman(int num) {
string s = "";
int digit = num / 1000; // 千位
if (digit > 0) {
for (int i = 0; i < digit; i++) {
s += 'M';
}
num %= 1000;
}
digit = num / 100; // 百位
if (digit > 0) {
if (digit == 4) {
s += "CD";
digit = 0;
} else if (digit == 9) {
s += "CM";
digit = 0;
} else if (digit > 4) {
s += 'D';
digit -= 5;
}
for (int j = 0; j < digit; j++) {
s += 'C';
}
num %= 100;
}
digit = num / 10; // 十位
if (digit > 0) {
if (digit == 4) {
s += "XL";
digit = 0;
} else if (digit == 9) {
s += "XC";
digit = 0;
} else if (digit > 4) {
s += 'L';
digit -= 5;
}
for (int j = 0; j < digit; j++) {
s += 'X';
}
num %= 10;
}
if (num > 0) { // 個位
if (num == 4) {
s += "IV";
num = 0;
} else if (num == 9) {
s += "IX";
num = 0;
} else if (num > 4) {
s += 'V';
num -= 5;
}
for (int j = 0; j < num; j++) {
s += 'I';
}
}
return s;
}
};
```
## 🧠 解題思路(二)用Greedy
這份寫法我用的是「把所有可能的羅馬符號組合先列出來,然後從大到小貪心地扣」。
因為羅馬數字其實可以被視為一種「固定面額的表示法」:
* 除了基本符號(M, D, C, L, X, V, I)以外
* 還有題目規則允許的減法符號(CM, CD, XC, XL, IX, IV)
只要我把這些符號按數值從大到小排好,接下來就可以:
* 只要 `num` 還大於等於目前的面額 `n[pos]`
* 就把對應符號 `a[pos]` 加進答案
* 並把 `num` 減掉這個面額
* 如果 `num` 小於目前面額
* 就換下一個較小的面額
這樣做會得到正確羅馬數字,原因是題目允許的表示法本來就是由這些面額組成,而且「每次優先用最大面額」會讓字串自然維持由大到小的順序,也會自動處理 4、9、40、90、400、900 這些減法情況。
### 所有變數
* `num`:題目輸入的整數(我會一路扣到 0)
* `a`:字串陣列,存每個面額對應的羅馬符號(已按數值由大到小排列)
* `n`:整數陣列,存每個羅馬符號對應的面額(與 `a` 同索引對齊)
* `s`:最後要回傳的羅馬數字字串
* `pos`:目前正在嘗試使用的面額索引(從 0 往後走,面額越來越小)
## 🪜 主要流程步驟
### 1. 先把所有「合法符號」按面額由大到小排好
我用兩個陣列對齊存資料:
* `n[pos]` 是面額(1000, 900, 500, ... , 1)
* `a[pos]` 是對應符號("M", "CM", "D", ... , "I")
這裡把減法符號(例如 900 的 "CM")也當作一種面額一起處理,後面就不用特判 4/9 這些情況。
---
### 2. 用 pos 從大面額一路往小面額掃
我從 `pos = 0` 開始(面額最大),只要 `num` 還沒變成 0 就一直做:
* 如果 `num >= n[pos]`
* 代表目前面額可以用
* 把 `a[pos]` 加到答案字串 `s`
* `num -= n[pos]`,把這一塊扣掉
* 注意這時 `pos` 不動,因為同一面額可能要用很多次(例如 3000 會用三次 "M")
* 否則(`num < n[pos]`)
* 代表這個面額太大,不能用
* `pos++` 換下一個較小面額繼續試
---
### 3. num 扣到 0 就結束,答案字串完成
當 `num` 變成 0,代表所有值都已經被表示完畢,回傳 `s`。
## 💻 程式碼實作(二)
```cpp
class Solution {
public:
string intToRoman(int num) {
string a[] = {"M", "CM", "D", "CD", "C", "XC", "L",
"XL", "X", "IX", "V", "IV", "I"};
int n[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string s = "";
int pos = 0;
while (num > 0) {
if (num >= n[pos]) {
s += a[pos];
num -= n[pos];
} else {
pos++;
}
}
return s;
}
};
```
## 🔗 題目連結
https://leetcode.com/problems/integer-to-roman/description/