티스토리 뷰

문제

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

 

3830번: 교수님은 기다리지 않는다

교수님의 질문 (? a b)이 입력으로 들어올 때 마다, 지금까지 측정한 결과를 바탕으로 a와 b의 무게 차이를 계산할 수 있다면, b가 a보다 얼마나 무거운지를 출력한다. 무게의 차이의 절댓값이 1,000,

www.acmicpc.net

 


알고리즘

  • Union-Find

풀이

배열 2개를 만들어 다음과 같이 정의합니다:

 

  • \(uf[i]:\) \(i\)가 속한 집합의 루트 노드
  • \(dist[i]:\) \(i - i\)가 속한 집합의 루트 노드

UNKNOWN인지 아닌지는 \(find\) 함수를 사용해 같은 집합에 있는지 아닌지 확인하면 쉽게 알 수 있습니다.

 

입력 받은 \(a, b\)의 차를 구할 때는 \(dist[b] - dist[a]\)를 구하면 됩니다.

여기서 \(dist[x]\)는 \(find\) 함수를 응용해서 \(x\)의 루트 노드를 구할 때마다 갱신해 주면 됩니다.

현 \(x\)에 대하여 \(dist[x]\)에는 \(x\)와 \(uf[x]\)의 차가 저장되어 있으므로 거기에 \(uf[x]\)와 \(uf[uf[x]]\)의 차, 즉 \(dist[uf[x]]\)를 더해주면 됩니다. 그럼 \(x\)의 최종적인 루트 노드에 대한 정보를 얻을 수 있습니다.

int find(int x) {
    if (uf[x] < 0) return x;
    int root = find(uf[x]);
    dist[x] += dist[uf[x]];
    return uf[x] = root;
}

두 집합을 입력으로 인해 합칠 때도 있는데 이때는 \(merge\) 함수를 사용합니다.

\(merge\)하기 전 \(a\)와 \(b\)의 루트 노드를 \(r_a, r_b\)라고 정의하겠습니다.

하나로 합쳐야 하므로 \(uf[r_b]\)에 \(r_a\)를 저장하고(\(r_b\)의 루트는 \(r_a\)가 됩니다.), \(\ dist[r_b]\)에는 \(dist[a] + w\), 즉 \(b\)와 \(r_a\)의 차에서 \(b\)와 \(r_b\)의 차인 \(dist[b]\)를 빼주면 \(r_b\)와 \(r_a\)의 차를 구할 수 있으므로 그 값을 저장합니다. (\(a\)와 \(b\)는 바뀌어도 상관없습니다.)

void merge(int a, int b, int w) {
    int root_a = find(a);
    int root_b = find(b);
    if (root_a == root_b) return;
    dist[root_b] = dist[a] - dist[b] + w;
    uf[root_b] = root_a;
}

여기서 \(r_a\)의 집합에 있었던 무게와 \(r_b\)의 집합에 있어던 무게의 차에 대해서는 들어온 쿼리에 대해 UNKNOWN인지 아닌지 확인하기 위해 \(find\) 함수를 호출하므로, 자연스럽게 갱신이 가능합니다.

 


전체 코드

#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int MXN = 100001;
int uf[MXN];
ll dist[MXN];

int find(int x) {
    if (uf[x] < 0) return x;
    int root = find(uf[x]);
    dist[x] += dist[uf[x]];
    return uf[x] = root;
}

void merge(int a, int b, int w) {
    int root_a = find(a);
    int root_b = find(b);
    if (root_a == root_b) return;
    dist[root_b] = dist[a] - dist[b] + w;
    uf[root_b] = root_a;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    while (1) {
        memset(uf, -1, sizeof(uf));
        memset(dist, 0, sizeof(dist));
        int N, M;
        cin >> N >> M;
        if (!N && !M) break;
        while (M--) {
            char c;
            int a, b, w;
            cin >> c;
            if (c == '!') {
                cin >> a >> b >> w;
                merge(a, b, w);
            }
            else {
                cin >> a >> b;
                if (find(a) != find(b)) cout << "UNKNOWN\n";
                else cout << dist[b] - dist[a] << '\n';
            }
        }
    }

    return 0;
}

'BOJ' 카테고리의 다른 글

[BOJ 1201] NMK  (0) 2021.08.07
[BOJ 2516] 원숭이  (2) 2021.08.06
[BOJ 1126] 같은 탑  (1) 2021.06.23
[BOJ 5444] 시리얼 넘버  (0) 2021.06.20
[BOJ 1854] K번째 최단경로 찾기  (1) 2021.06.13
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함