알고리즘/문제 풀이

백준 10478번: 단위

Themion 2022. 1. 26. 15:37

https://www.acmicpc.net/problem/10478

 

10478번: 단위

과학에서 단위는 어디에서나 존재한다. 물리에서 단위는 거리(미터, 키로미터 등), 무게(키로그램, 그램 등), 그리고 다른 많은 양을 측정하는데 사용한다. 컴퓨터 과학자들은 용량의 단위(키로

www.acmicpc.net

각 단위를 노드, 단위 간의 배율을 에지로 보면 이 문제는 가장 큰 단위, 즉 루트 노드에서 다른 노드로의 배율을 각각 출력하는 문제이다. 단 루트 노드가 존재하더라도 어떤 노드가 루트 노드인지 입력만 가지고는 알 수 없으므로, 플로이드-워셜을 이용해 모든 노드에서 모든 노드로의 배율을 계산한 뒤 가장 큰 배율을 가진 노드를 루트 노드로 간주한다.

#include <iostream>
#include <map>
#include <string>

using namespace std;

#define MAX_N 10
#define FOR(i, a, b) for(i = a; i < b; i++)

// arr[i][j]: 단위 i가 단위 j의 정수배라면 그 배수, 그 반대라면 -1
int arr[MAX_N][MAX_N];

// 단위 i와 단위 j의 비율을 val로 설정
void set_arr(int i, int j, int val) {
    arr[i][j] = val;
    arr[j][i] = -1;
}

void test_case(int N) {
    // i, j, k: 순회에 사용할 인덱스, num: 각 단위 사이의 배율, max: num의 최댓값
    int i, j, k, num, max = 0;
    // from, to: 문제에서 주어지는 단위의 이름을 입력할 공간
    // name[i]: i번째 단위의 이름
    string from, to, name[MAX_N];
    // unit[str]: 단위 str의 순서
    map<string, int> unit;

    // 각 단위의 이름을 입력받고 name, unit에 저장한 뒤 arr를 초기화
    FOR(i, 0, N) {
        cin >> name[i];
        unit[name[i]] = i;
        FOR(j, 0, N) arr[i][j] = i == j;
    }

    // 각 단위 사이의 배율을 입력받는다
    FOR(i, 1, N) {
        cin >> from >> to >> num >> to;
        arr[unit[from]][unit[to]] = num;
        arr[unit[to]][unit[from]] = -1;
    }

    // 플로이드-워셜을 응용해 arr에 각 단위간의 배율을 저장
    // 두 단위 i, j의 직접적인 관계를 모를 때
    // 두 단위와의 직접적인 관계를 아는 k를 이용해 두 단위의 직접적인 관계를 계산
    FOR(k, 0, N) FOR(i, 0, N) FOR(j, 0, N) if (!arr[i][j]) {
        if (arr[i][k] > 0 && arr[j][k] > 0) {
            if (arr[i][k] > arr[j][k]) set_arr(i, j, arr[i][k] / arr[j][k]);
            else set_arr(j, i, arr[j][k] / arr[i][k]);
        }
        else if (arr[i][k] > 0 && arr[j][k] < 0)
            set_arr(i, j, arr[i][k] * arr[k][j]);
        else if (arr[i][k] < 0 && arr[j][k] > 0)
            set_arr(j, i, arr[j][k] * arr[k][i]); 
        else if (arr[i][k] < 0 && arr[j][k] < 0) {
            if (arr[k][i] > arr[k][j]) set_arr(j, i, arr[k][i] / arr[k][j]);
            else set_arr(i, j, arr[k][j] / arr[k][i]);
        }
    }

    // 배율이 가장 큰 관계를 가진 단위의 인덱스를 k에 저장한 뒤
    FOR(i, 0, N) FOR(j, 0, N) {
        if (max < arr[i][j]) {
            max = arr[i][j];
            k = i;
        }
        else if (max < arr[j][i]) {
            max = arr[j][i];
            k = j;
        }
    }

    // 가장 큰 단위를 출력하고
    cout << 1 << name[k];
    arr[k][k] = 0;

    // 두번째로 큰 단위부터 내림차순으로 배율과 단위의 이름을 출력
    for (int _ = 1; _ < N; _++) {
        // 배율이 작은 순서대로 출력해야 한다
        num = __INT_MAX__;
        // 배율이 가장 작으면서 그 배율이 1 초과인 단위의 인덱스를 i에 저장한 뒤
        FOR(j, 0, N) if (num >= arr[k][j] && arr[k][j] > 1) {
            num = arr[k][j];
            i = j;
        }
        // " = (단위 배율)(단위 이름)"을 각각 출력한 뒤
        cout << " = " << arr[k][i] << name[i];
        // 중복 출력을 막기 위해 단위 배율을 0으로 변경
        arr[k][i] = 0;
    }

    // 개행 문자를 출력해 출력을 종료
    cout << '\n';

    return;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    // 테스트 케이스의 단위 수를 입력받고 단위 수가 0이 아니라면 테스트 케이스 실행
    for (int N; cin >> N && N; ) test_case(N);

    return 0;
}

'알고리즘 > 문제 풀이' 카테고리의 다른 글

백준 3151번: 합이 0  (0) 2022.02.06
백준 23349번: 졸업 사진  (0) 2022.02.03
백준 11657번: 타임머신  (0) 2022.01.25
백준 1520번: 내리막 길  (0) 2022.01.25
백준 19598번: 최소 회의실 개수  (0) 2022.01.23