# 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/