문제
N개의 도시와 연결 정보가 주어지고, 여행 계획에 포함된 도시들이 모두 연결되어 있는지 판별하는 문제다.
여행 계획에 적힌 도시가 서로 직접 연결될 필요는 없다. 중간 도시를 거쳐 갈 수 있으면 같은 이동 가능 구역에 속한다. 따라서 실제 경로를 매번 찾기보다, 연결된 도시들을 미리 하나의 집합으로 묶고 여행지들의 대표가 같은지만 확인하면 된다.
Union-Find는 각 집합의 대표 원소를 저장하는 자료구조다. 도로가 있는 두 도시의 집합을 합치고, 마지막에 여행 계획의 도시들이 같은 대표를 갖는지 비교한다.
핵심 아이디어
- 도시 간 직접 이동뿐 아니라 다른 도시를 경유한 이동도 가능하다.
- 따라서 같은 연결 요소(컴포넌트)에 속하는지만 확인하면 된다.
- Union-Find로 연결된 도시들을 합치고, 여행 계획의 연속된 도시 쌍이 같은 집합인지 확인한다.
풀이
#include <iostream>
using namespace std;
int cityCount, planCityCount, isLinked;
int parents[201];
int find_parent(int x) {
if (parents[x] == x) return x;
return parents[x] = find_parent(parents[x]);
}
void set_union(int a, int b) {
a = find_parent(a);
b = find_parent(b);
if (a > b) parents[a] = b;
else parents[b] = a;
}
bool same_set(int a, int b) {
return find_parent(a) == find_parent(b);
}
int main() {
for (int i = 0; i < 201; i++) parents[i] = i;
cin >> cityCount >> planCityCount;
for (int i = 1; i <= cityCount; i++) {
for (int j = 1; j <= cityCount; j++) {
cin >> isLinked;
if (isLinked) set_union(i, j);
}
}
int connectionCount = 1;
int prevCity, currentCity;
cin >> prevCity;
for (int i = 1; i < planCityCount; i++) {
cin >> currentCity;
if (same_set(prevCity, currentCity)) {
connectionCount++;
prevCity = currentCity;
}
}
cout << (connectionCount == planCityCount ? "YES" : "NO");
return 0;
}
주요 포인트
- 인접 행렬로 연결 정보가 주어지므로
isLinked == 1이면set_union으로 합친다. - 여행 계획의 연속된 도시 쌍만 확인하면 충분하다 — 같은 컴포넌트 내에서는 어떤 경로든 이동 가능하기 때문이다.
- 경로 압축(
parents[x] = find_parent(parents[x]))으로 효율을 확보한다.
복잡도
- 시간: O(N^2 × α(N)) — 인접 행렬 순회 + Union 연산
- 공간: O(N)
Union-Find의 실전 응용, “연결되어 있는가?”라는 질문을 효율적으로 답하는 문제다.
댓글