[伯俊]2608/ローマ数字


# Appreciation

/*
 * Problem :: 2608 / 로마 숫자
 *
 * Kind :: Math
 *
 * Insight
 * - 수를 만드는 규칙이 좀 복잡하다
 *   차근차근 살펴보자
 *   + 로마 숫자 => 아라비아 숫자
 *     # 로마 숫자의 각 숫자가 가지는 값을 모두 더한다
 *       다만, IV, IX, XL, XC, CD, CM 과 같은 예외가 존재하는데
 *       이 경우는 첫 번째 로마 숫자가 두 번째 로마 숫자보다 값이 작다
 *       (이들 외에는 작은 숫자가 큰 숫자 왼쪽 어디에도 나올 수 없다)
 *       -> 마주치는 로마 숫자의 각 숫자값을 모두 더하되
 *          이전의 로마숫자값이 작으면 그 이전에 더해진 로마숫자값을 포함하여 그 값을 2번 빼준다
 *   + 아라비아 숫자 => 로마 숫자
 *     주어진 값이 val 이라면
 *     # 천의 자리
 *       천의 자리수 d4 = val / 1000
 *       d4 만큼 'M'을 추가한다
 *     # 백의 자리
 *       val %= 1000
 *       d3 = val / 100
 *       d3 가 4 또는 9 인지 확인한다 <= CD, CM
 *       d3 가 5 이상인지 확인한다 <= D
 *       남은 d3 만큼 'C'를 추가한다
 *     # 십의 자리
 *       val %= 100
 *       d2 = val / 10
 *       d2 가 4 또는 9 인지 확인한다 <= XL, XC
 *       d2 가 5 이상인지 확인한다 <= L
 *       남은 d2 만큼 'X'를 추가한다
 *     # 일의 자리
 *       val %= 10
 *       d1 = val
 *       d1 가 4 또는 9 인지 확인한다 <= IV, IX
 *       d1 가 5 이상인지 확인한다 <= V
 *       남은 d1 만큼 'I'를 추가한다

 * Point
 * - 문제 설명이 꽤 복잡하지만
 *   로마 숫자에서 아라비아 숫자 변환에 관련된 부분을 먼저 다루고
 *   아라비아 숫자에서 로마 숫자의 변환에 관련된 부분을 나중에 다루는 방식으로
 *   나누어 읽으면 쉽게 필요한 정보를 뽑아낼 수 있는 것 같다
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <map>

using namespace std;

#define endl '\n'

// Set up : Global Variables
map<char,int> V;

// Set up : Functions Declaration
int decoder(string r);
string encoder(int val);


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Process
    /* 로마숫자값 Map 에 저장 */
    V['I'] = 1;
    V['V'] = 5;
    V['X'] = 10;
    V['L'] = 50;
    V['C'] = 100;
    V['D'] = 500;
    V['M'] = 1000;

    // Set up : Input
    string r1, r2;
    cin >> r1 >> r2;

    // Process
    int a1 = decoder(r1);
    int a2 = decoder(r2);

    // Control : Output
    cout << a1+a2 << endl;
    cout << encoder(a1+a2) << endl;
}

// Helper Functions
int decoder(string r)
/* 주어진 로마 숫자에 해당하는 아라비아 숫자를 반환 */
{
    int val = 0;
    for (int i=0; i<r.length(); i++) {
        val += V[r[i]]; /* 마주치는 로마 숫자의 숫자값을 더함 */
        /* 이전의 로마숫자값이 현재의 로마숫자값보다 작으면 */
        if (i > 0 && V[r[i-1]] < V[r[i]]) {
            val -= 2*V[r[i-1]]; /* IV, IX, XL, XC, CD, CM 과 같은 예외에 해당
                                 * 이전의 로마숫자값이 더해진 상태이므로, 그 값을 2번 빼줌 */
        }
    }

    return val;
}

string encoder(int val)
/* 주어진 아라비아 숫자에 해당하는 로마 숫자를 반환 */
{
    string rome;

    /* 천의 자리 */
    int d4 = val / 1000; /* 천의 자리수 */
    val %= 1000;
    /* d4 는 0~3 의 값을 가질 수 있음
     * IV, IX, XL, XC, CD, CM 과 같은 예외도 없음
     * 그냥 d4 만큼 'M' 추가 */
    for (int i=0; i<d4; i++) {
        rome.push_back('M');
    }

    /* 백의 자리 */
    int d3 = val / 100; /* 백의 자리수 */
    val %= 100;
    if (d3 == 4 || d3 == 9) { /* CD, CM 예외 처리 */
        rome.push_back('C');
        if (d3 == 4) { rome.push_back('D'); }
        if (d3 == 9) { rome.push_back('M'); }
    } else {
        if (d3 >= 5) { rome.push_back('D'); d3 -= 5; } /* D 처리 */
        for (int i=0; i<d3; i++) { /* 남은 d3 만큼 'C' 추가 */
            rome.push_back('C');
        }
    }

    /* 십의 자리 */
    int d2 = val / 10; /* 십의 자리수 */
    val %= 10;
    if (d2 == 4 || d2 == 9) { /* XL, XC 예외 처리 */
        rome.push_back('X');
        if (d2 == 4) { rome.push_back('L'); }
        if (d2 == 9) { rome.push_back('C'); }
    } else {
        if (d2 >= 5) { rome.push_back('L'); d2 -= 5; } /* L 처리 */
        for (int i=0; i<d2; i++) { /* 남은 d2 만큼 'X' 추가 */
            rome.push_back('X');
        }
    }

    /* 일의 자리 */
    int d1 = val; /* 일의 자리수 */
    if (d1 == 4 || d1 == 9) { /* IV, IX 예외 처리 */
        rome.push_back('I');
        if (d1 == 4) { rome.push_back('V'); };
        if (d1 == 9) { rome.push_back('X'); }
    } else {
        if (d1 >= 5) { rome.push_back('V'); d1 -= 5; } /* V 처리 */
        for (int i=0; i<d1; i++) { /* 남은 d1 만큼 'I' 추가 */
            rome.push_back('I');
        }
    }

    return rome;
}