본문 바로가기

Algorithm/알고리즘 문제

백준 2608번 : 로마 숫자

문제


2608번: 로마 숫자 (acmicpc.net)

 

2608번: 로마 숫자

첫째 줄과 둘째 줄에 하나씩 로마 숫자로 표현된 수가 주어진다. 입력된 각 수는 2000 보다 작거나 같고, 두 수의 합은 4000보다 작다.

www.acmicpc.net

주어진 로마 숫자의 합을 정수와 로마 숫자로 출력하는 문제

 

아이디어


🔸로마문자 to 정수 : 해시맵 이용

순방향의 경우 바로 정수로 바꾸고, 역방향의 경우 다음 문자와 결합하여 정수로 바꿈
마지막 문자는 바로 정수로 바꿈
주어진 로마자가 하나인 경우 구현한 방향 로직을 실행할 수 없으므로 따로 빼주어서 바로 정수로 바꿈

 🔸정수 to 로마문자 : 배열 2개 이용

키를 기준으로 정렬된 해시맵을 구현하기 위해서 정적배열 2개 이용
각 인덱스끼리 매핑되게 하면, '정렬된 해시맵'처럼 사용할 수 있음
while 문을 돌면서 num가 최소한의 문자로 바뀔 수 있게 함

헷갈렸던 부분 : 노란색으로 표시한 'V,C,D는 한번만 사용할 수 있고, I, X,C,M은 연속 세번까지만 사용 가능', 'IX와 IV는 같이 쓸 수 없음' 등의 조건을 어떻게 구현해야할지 고민했지만 생각해보면, 큰 수부터 로마자로 바꾸면 발생할 수 없는 조건이기 때문에 구현하지 않아도 되는 것이었음

package javaPractice.BJ2608;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

public class Main {

    public static int romeToInt(String[] r1){
        HashMap<String, Integer> hm = new HashMap<>();
        {
            hm.put("I", 1);
            hm.put("V", 5);
            hm.put("X", 10);
            hm.put("L", 50);
            hm.put("C", 100);
            hm.put("D", 500);
            hm.put("M", 1000);
            hm.put("IV", 4);
            hm.put("IX", 9);
            hm.put("XL", 40);
            hm.put("XC", 90);
            hm.put("CD", 400);
            hm.put("CM", 900);
        }

        if(r1.length == 1){
            return hm.get(r1[0]);
        }

        int result = 0;

        // 로직은 좋으나 r1가 한자리수인 경우 실행되지 않으므로 따로 처리해줘야 함
        for (int i = 0; i < r1.length-1; i++) {
            if(hm.get(r1[i]) >= hm.get(r1[i+1])){ // 순방향이면 그대로 정수로 바꾸기
                result += hm.get(r1[i]);
            } else {
                result += hm.get((r1[i])+r1[i+1]); // 역방향이면 다음 수랑 결합한 것을 정수로 바꾸기
                i++;
            }
            if(i == r1.length-2){
                result += hm.get(r1[r1.length-1]); // 마지막 idx에 도달하면 그대로 정수로 바꾸기
            }
        }

        return result;
    }

    public static String intToRome(int num){
        StringBuilder result = new StringBuilder();
        
        // 해시맵을 이용하려 했으나, 해시맵은 정렬을 보장하지 않으므로 크기 순서대로 꺼낼 수 없다는 단점이 있음
        // => 두개의 배열을 이용하면 됨
        // 내가 정적 배열 두개를 만들 수 있고, 각 인덱스가 매핑되게 하면, '키를 기준으로 정렬된 해시맵'처럼 사용할 수 있는 것!
        String[] rome = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        
        int i = 0;
        while(num > 0){
            while(num >= values[i]){
                num-=values[i];
                result.append(rome[i]);
            }
            i++;
        }
        return result.toString();
    }

    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String[] r1 = bf.readLine().split("");
        String[] r2 = bf.readLine().split("");

        int sum = romeToInt(r1) + romeToInt(r2);
        String result = intToRome(sum);

        System.out.println(sum);
        System.out.println(result);
    }
}