문제
서울 지하철 2호선은 다음과 같이 생겼다.
지하철 2호선에는 51개의 역이 있고, 역과 역 사이를 연결하는 구간이 51개 있다. 즉, 정점이 51개이고, 양방향 간선이 51개인 그래프로 나타낼 수 있다. 2호선은 순환선 1개와 2개의 지선으로 이루어져 있다. 한 역에서 출발해서 계속 가면 다시 출발한 역으로 돌아올 수 있는 노선을 순환선이라고 한다. 지선은 순환선에 속하는 한 역에서 시작하는 트리 형태의 노선이다.
두 역(정점) 사이의 거리는 지나야 하는 구간(간선)의 개수이다. 역 A와 순환선 사이의 거리는 A와 순환선에 속하는 역 사이의 거리 중 최솟값이다.
지하철 2호선과 같은 형태의 노선도가 주어졌을 때, 각 역과 순환선 사이의 거리를 구해보자.
입력
첫째 줄에 역의 개수 N(3 ≤ N ≤ 3,000)이 주어진다. 둘째 줄부터 N개의 줄에는 역과 역을 연결하는 구간의 정보가 주어진다. 같은 구간이 여러 번 주어지는 경우는 없고, 역은 1번부터 N번까지 번호가 매겨져 있다. 임의의 두 역 사이에 경로가 항상 존재하는 노선만 입력으로 주어진다.
출력
총 N개의 정수를 출력한다. 1번 역과 순환선 사이의 거리, 2번 역과 순환선 사이의 거리, ..., N번 역과 순환선 사이의 거리를 공백으로 구분해 출력한다.
문제 풀이
이 문제는 2개의 step으로 나눠서 문제를 해결하면 된다.
1. 사이클에 포함되는 역 확인
2. 사이클에 포함되지 않는 역은 사이클과 몇 정거장 떨어져 있는지 확인
1. 사이클에 포함되는 역 확인
경로가 중요한 문제이기 때문에 dfs를 이용하여 문제를 해결하였다.
시작 경로는 무조건 1번 역으로 지정하였다.
1번 역 부터 시작하여, 이어지는 역을 차례대로 방문하면서 stack에 집어 넣는다.
더 이상 이어지는 역이 없을 경우 이전 분기점까지 pop을 한다.
분기점 부터 방문하지 않은 다음 역을 계속해서 방문하고, 똑같은 역을 두 번 방문한 경우가 사이클을 한번 돈 경우가 되는 것이다.
1 -> 2 -> 3 -> 1 (사이클 확인)
stack에 쌓여있는 1, 2, 3 을 역순으로 확인 하면서 1번역이 나올 때 까지 (똑같은 역을 두번 방문한 지점이 사이클의 시작점이자 끝 지점이다.) isCycle = true; 로 표시해준다.
이제는 사이클로 부터 bfs를 이용하여 사이클이 아닌 지점이 사이클로 부터 얼마나 떨어져 있는지를 확인하면 된다.
import java.beans.Visibility;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
import java.util.StringTokenizer;
public class baekjoon16947_3 {
private static ArrayList<ArrayList<Integer>> info;
private static int[] distInfo;
private static boolean[] visit;
private static boolean[] isCycle;
private static int n;
private static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
distInfo = new int[n + 1];
visit = new boolean[n + 1];
isCycle = new boolean[n + 1];
info = new ArrayList<>();
for (int i = 0; i < n + 1; i++) {
info.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
int from = Integer.parseInt(st.nextToken());
int to = Integer.parseInt(st.nextToken());
info.get(from).add(to);
info.get(to).add(from);
}
Stack<Integer> stack = new Stack<>();
stack.add(1);
visit[1] = true;
findCycle16947(stack, 1);
distInfo = new int[n + 1];
visit = new boolean[n + 1];
for (int i = 1; i < n + 1; i++) {
if (isCycle[i]) {
calculateDistance(i);
break;
}
}
StringBuilder sb = new StringBuilder();
for (int i = 1; i < n + 1; i++) {
sb.append(distInfo[i] + " ");
}
System.out.println(sb);
}
private static void calculateDistance(int currentStation) {
Queue<Integer> queue = new LinkedList<>();
queue.add(currentStation);
while (!queue.isEmpty()) {
currentStation = queue.remove();
for (Integer nextStation : info.get(currentStation)) {
if (!visit[nextStation]) {
visit[nextStation] = true;
if (!isCycle[nextStation]) {
distInfo[nextStation] = distInfo[currentStation] + 1;
}
queue.add(nextStation);
}
}
}
}
private static void findCycle16947(Stack<Integer> stack, int currentStation) {
for (Integer nextStation : info.get(currentStation)) {
// toDo : calculate result;
if (visit[nextStation] && Math.abs(distInfo[nextStation] - distInfo[currentStation]) >= 2) {
int endCycle = nextStation;
int endIndex = stack.size() - 1;
isCycle[endCycle] = true;
while (stack.contains(endCycle) && stack.get(endIndex) != endCycle) {
isCycle[stack.get(endIndex)] = true;
endIndex--;
}
}
if (!visit[nextStation]) {
distInfo[nextStation] = distInfo[currentStation] + 1;
visit[nextStation] = true;
stack.add(nextStation);
findCycle16947(stack, nextStation);
stack.pop();
}
}
}
}
'알고리즘 > 초급1' 카테고리의 다른 글
[JAVA] 백준 16964번 : DFS 스페셜 저지 ( 초급 2-47 ) - hard (0) | 2021.10.21 |
---|---|
[JAVA] 백준 16940번: BFS 스페셜 저지 ( 초급 2-46 ) - fail (0) | 2021.10.21 |
[JAVA] 백준 16929번: Two Dots ( 초급 2-44 ) (0) | 2021.10.19 |
[JAVA] 백준 7562번: 나이트의 이동 ( 초급 2-43 ) (0) | 2021.10.19 |
[JAVA] 백준 7576번: 토마토 ( 초급 2-42 ) (0) | 2021.10.19 |