02 Aug 2020

[BOJ 13344]

Chess Tournament

문제 바로가기 : https://www.acmicpc.net/problem/13344
문제 설명은 위 링크에서 확인해주시길 바랍니다.

Solution


review

이 문제에서는 연산 기호 =, >가 등장하는데 a>b이면 b앞에 a가 온다고 보면 되겠다. 순서가 정해지는 셈이다.
a=b는 a와 b의 순서가 상관이 없음을 의미한다고 결론내렸다. TC를 분석하면서 알게된 점은 다음과 같다.

1. 출력은 일관성(consistent)이 있는지 없는지를 출력할 뿐, 순서를 출력하지 않는다.
애초에 문제가 개인별 skill level의 consistent를 확인하는거여서 등수 출력X
-> 등수를 출력한다면 조금 까다로워질 수 있지만 덕분에 간단하게 답을 내릴 수 있었다.

2. 위상 정렬은 진입 차수가 0일 때 큐에 집어넣는 연산을 한다.
근데 equal 연산일 때 양쪽의 진입 차수를 증가시키면 진입 차수가 0인 노드가 존재할 수 없다.
-> (물론 그렇지 않을 수 있지만) 위상 정렬만으로는 해결할 수 없다는 결론을 내렸다.

1, 3번째 TC에서 inconsistent가 나타나는 유형은 같다.
이미 서로의 skill level이 같다고 나와있는데 > 연산이 등장하기 때문이다.
0=1, 1>0 or 0>1 이렇게만 나온다면 inconsistent 판별하기 쉽겠지만
TC에서는 skill level이 같음을 간접적으로 표현하고 있다.

TC 1.
1 = 2, 0 = 2
: 0 = 1 = 2

TC 2.
0 = 1, 1 = 2, 3 = 4
: 0 = 1 = 2, 3 = 4

직접적으로 언급되지 않아도 같다는걸 표현해줄 필요가 있다. make_union()으로 같은 집합으로 만들어줬다.
그리고 서로 같을 때 > 연산이 들어오면 무조건 inconsistent라는건 자명하다.
이 경우 위상 정렬(topology())까지 들어갈 필요가 없다.

//K > L
for (int i = 0; i < tmp.size(); i++) {
    int K = find_parent(tmp[i].K);
    int L = find_parent(tmp[i].L);

    //서로 같은 집합인데 > 연산이 들어오면 -> inconsistent
    if (find_parent(K) == find_parent(L)) answer = false;

    node[K].push_back(L);
    node[L].push_back(K);
    degree[L]++;
}

//바로 종료시켜주자
if (!answer) {
    cout << "inconsistent";
    return 0;
}

이후에는 위상 정렬를 이용하는데 위에서 집합이 만들어져서 비교 대상에서 자식은 제외된다.
등수를 출력할 필요가 없기 때문에 총 수(n)에서 자식(childs)을 제외한 값이 ans에 들어가야 한다.
때문에 이 조건을 만족하지 못하면 inconsistent, 만족하면 consistent가 들어간다.

topology(n); //위상 정렬
if (ans.size() != n-childs) answer = false;

//answer
if (!answer) cout << "inconsistent";
else cout << "consistent";

근데 문제가 있다. K>L을 처리할 때 위 방법대로 하면 각 K, L의 부모를 가지고 비교를 하게 된다.
때문에 degree가 불필요하게 증가할 수 있다. 또한 해당 노드에 중복된 이웃이 push될 수도 있다.

//K > L
for (int i = 0; i < tmp.size(); i++) {
    int K = find_parent(tmp[i].K);
    int L = find_parent(tmp[i].L);

    if (find_parent(K) == find_parent(L)) answer = false;
    //언급하고 있는 부분
    node[K].push_back(L);
    node[L].push_back(K);
    degree[L]++;
}

중복 입력을 방지할 방법을 동원해도 되지만(동원된다면 문제 해결이 가능하다) 좋은 방법이 떠오르지 않았다.
그래서 나는 기존의 위상 정렬 함수에서 소스를 조금 수정해서 처리했다.

void topology(int n) {
    queue<int> q;

    //진입 차수가 0인 노드를 삽입
    for (int i = 0; i < n; i++)
        if (degree[i] == 0)
            q.push(i);

    while (!q.empty()) {
        int cur = q.front(); q.pop();
        chk[cur] = true;
        ans.push_back(cur);

        //이웃된 노드의 간선을 모두 삭제
        for (auto& next : node[cur]) {
            if (!chk[next]) {
                //chk[next] = true; <= 여러번 입력 된만큼 빼줘야 하기 때문에 체크 기능 해제
                degree[next]--;
                //진입 차수가 0이면 큐에 삽입
                if (degree[next] == 0) {
                    q.push(next);
                }
            }
        }
    }
}


좋은 방법이 여러가지 있을 수 있겠지만 생각한대로 구현한게 통과돼서 운이 좋았다.
생각보다 별거 아닐줄 알았는데 생각보다 코드가 길어졌다.. 괜히 플래티넘이 아니구나ㅎㅎ

Location

Icheon, KR

Email

iteratively@naver.com

Social

-->