#1976 여행 가자
난이도 : 골드 4
유형 : 그래프/ union-find
▸ 문제
동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 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개의 정수가 주어진다. i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보를 의미한다. 1이면 연결된 것이고 0이면 연결이 되지 않은 것이다. A와 B가 연결되었으면 B와 A도 연결되어 있다. 마지막 줄에는 여행 계획이 주어진다. 도시의 번호는 1부터 N까지 차례대로 매겨져 있다.
▸ 출력
첫 줄에 가능하면 YES 불가능하면 NO를 출력한다.
문제 풀이 🖋
그래프 탐색 여부를 판별하는 문제이다. 이는 어설프게 접근했다가는 헷갈릴 수 있는데 그래프를 직접 탐색하라는 문제가 아니다. 그래프 정점간의 관계를 알아내야 한다. 여러 그래프 알고리즘으로 풀 수 있겠지만 가장 문제 의도와 가까운 Union-find 서로소 집합을 사용하는게 가장 적절한 풀이이다.
📚 조건
∙ 도시의 수 N (1 <= N <= 200)
∙ 여행 계획에 속한 도시들의 수 M (1 <= M <= 1,000)
∙ 1이면 연결, 0이면 연결x. 양방향 그래프
∙ 도시 번호는 1 ~ N 차례대로 매겨져 있다.
서로소 집합 알고리즘을 사용하여 풀이했다.
1) 일단 가장 첫 번째 도시인 1을 루트로 지정한다.
2) 행렬 값이 1이면 a,b의 도시가 연결되어 있으므로 union(a,b)연산을 하여 각 도시의 부모 노드(parents[i])를 지정한다.
> 1이 루트노드이므로 작은 값이 부모 노드, 큰 값이 자식 노드가 되도록 설정했다.
if(x<y) {
parents[y]= x;
}else {
parents[x] = y;
}
3) 여행 계획으로 주어진 도시들을 보며 각 도시들의 부모노드가 일치하는지 확인한다. 만약, 일치하지 않으면 두 도시간의 연결이 되어 있지 않으므로 "NO"를 출력해주면 된다.
ex) 만약 A-B, B-C, A-D, B-D, E-A인 그래프가 있을 때 union연산을 하여 경로 압축을 하면 다음과 같이 나타낼 수 있다.
city | A(1) | B(2) | C(3) | D(4) | E(5) |
parents | 1 | 1 | 1 | 1 | 1 |
→ 이는 모두 1번 A도시와 연결되어 있다는 뜻으로 모든 루트 노드가 같은 도시들의 집합은 여행이 가능하다.
따라서, (A, B, C, D, E) 도시 집합 안에서 어느 조합으로 경로를 지정해도 모두 여행이 가능하다는 것을 알려준다.
풀이 코드 ✔︎
import java.io.*;
import java.util.StringTokenizer;
public class Main {
static int n,m;
static int[] parents;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = null;
int n = Integer.parseInt(br.readLine());
int m = Integer.parseInt(br.readLine());
int[] target = new int[m];
parents = new int[n+1];
for(int i=1; i<n+1; i++) {
parents[i] = i;
}
for(int i=1; i<n+1; i++) {
st = new StringTokenizer(br.readLine());
for(int j=1; j<n+1; j++) {
int num = Integer.parseInt(st.nextToken());
if(num ==1) {
union(i,j);
}
}
}
st = new StringTokenizer(br.readLine());
int root = find(Integer.parseInt(st.nextToken()));
for(int i=0; i<m-1; i++) {
int num = Integer.parseInt(st.nextToken());
if(root != find(num)) {
System.out.println("NO");
return;
}
}
System.out.println("YES");
}
static int find(int x) {
if(x == parents[x]) return x;
int rx = find(parents[x]);
return rx;
}
static void union(int x, int y) {
x = find(x);
y = find(y);
if(x<y) {
parents[y]= x;
}else {
parents[x] = y;
}
}
}
'Dot Algo∙ DS > PS' 카테고리의 다른 글
[BOJ] 백준 13302번 리조트 (Java) (0) | 2021.06.08 |
---|---|
[BOJ] 백준 2342번 Dance Dance Revolution (Java) (0) | 2021.06.08 |
[BOJ] 백준 2666번 벽장문의 이동 (Java) (0) | 2021.06.07 |
[BOJ] 백준 16395번 파스칼의 삼각형 (Java) (0) | 2021.06.07 |
[BOJ] 백준 1525번 퍼즐 (Java) (0) | 2021.06.06 |