BOJ 1976
위 문제는 백준 사이트의 알고리즘 1976문제에 관한 설명입니다.
동혁이는 친구들과 함께 여행을 가려고 한다.
한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다.
동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인지 알아보자.
물론 중간에 다른 도시를 경유해서 여행을 할 수도 있다.
예를 들어 도시가 5개 있고, A-B, B-C, A-D, B-D, E-A의 길이 있고,
동혁이의 여행 계획이 E C B C D 라면 E-A-B-C-B-C-B-D라는 여행경로를 통해 목적을 달성할 수 있다.
도시들의 개수와 도시들 간의 연결 여부가 주어져 있고,
동혁이의 여행 계획에 속한 도시들이 순서대로 주어졌을 때(중복 가능) 가능한지 여부를 판별하는 프로그램을 작성하시오.
입력
첫 줄에 도시의 수 N이 주어진다. N은 200이하이다.
둘째 줄에 여행 계획에 속한 도시들의 수 M이 주어진다. M은 1000이하이다.
다음 N * N 행렬을 통해 임의의 두 도시가 연결되었는지에 관한 정보가 주어진다.
1이면 연결된 것이고 0이면 연결이 되지 않은 것이다.
A와 B가 연결되었으면 B와 A도 연결되어 있다.
마지막 줄에는 여행 계획이 주어진다.
출력
첫 줄에 가능하면 YES 불가능하면 NO를 출력한다.
소스코드
import java.util.Scanner;
public class Main {
private static int[] parent;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
int M = scanner.nextInt();
int[][] map = new int[N + 1][N + 1];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N ; j++) {
map[i][j] = scanner.nextInt();
}
}
int[] plan = new int[M + 1];
for (int i = 1; i <= M; i++) {
plan[i] = scanner.nextInt();
}
parent = new int[N + 1];
for (int i = 1; i <= N; i++) {
parent[i] = i;
}
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (map[i][j] == 1) {
union(i, j);
}
}
}
int index = find(plan[1]);
for (int i = 2; i < plan.length; i++) {
if (index != find(plan[i])) {
System.out.println("NO");
return;
}
}
System.out.println("YES");
}
private static int find(int n) {
if (parent[n] == n) {
return n;
}
int p = find(parent[n]);
parent[n] = p;
return p;
}
private static void union(int a, int b) {
int x = find(a);
int y = find(b);
if (x != y) {
parent[x] = y;
}
}
}
이번 문제는 Union-Find을 사용했습니다.
Disjoint Set이란
서로 중복되지 않는 부분 집합들 로 나눠진 원소들에 대한 정보를 저장하고 조작하는 자료구조입니다.
즉, 공통 원소가 없는, 즉 “상호 배타적” 인 서로소 집합 자료구조입니다.
이것을 표현하기 위해서 사용되는 것이 Union-Find입니다.
Union-Find는 크게 3가지로 구성됩니다.
1. make-set(n)
parent배열의 인덱스번째 방에 인덱스 값을 넣어 유일한 원소로 구성된 새로운 집합을 만듭니다.
parent = new int[N + 1];
for (int i = 1; i <= N; i++) {
parent[i] = i;
}
2.find(x)
x가 속한 집합의 대표값(루트 노드 값)을 반환합니다. 즉, x가 어떤 집합에 속해 있는지 찾는 연산
private static int find(int n) {
if (parent[n] == n) {
return n;
}
int p = find(parent[n]);
parent[n] = p;
return p;
}
3.union(x, y)
x가 속한 집합과 y가 속한 집합을 합치는 연산으로 이 과정에서 Find가 활용됩니다.
private static void union(int a, int b) {
int x = find(a);
int y = find(b);
if (x != y) {
parent[x] = y;
}
}
문제 풀이부분
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (map[i][j] == 1) {
union(i, j);
}
}
}
받았던 map
의 값 중 1인곳이 있다면 그곳은 연결된부분이기에 union
합니다.
int index = find(plan[1]);
for (int i = 2; i < plan.length; i++) {
if (index != find(plan[i])) {
System.out.println("NO");
return;
}
}
여행계획의 첫머리 부분을 구해서 index
에 넣고, for문을 2번부터 끝까지 다 돌려 봅시다.
만약 나머지 여행계획들의 find
를 했을 때 다르다면?
연결이 끊어진 상태이므로 여행이 불가능합니다.
따라서 그때는 No를 출력하고 가능하다면 Yes를 출력합니다.