일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백트래킹
- kruskal
- 누적 합
- Linux
- Meet in the middle
- 위상 정렬
- 투 포인터
- BFS
- 이분 탐색
- 페르마 소정리
- 위상정렬
- concurreny
- union-find
- tarjan
- DP
- Java
- miller-rabin
- DFS
- 구현
- spring boot
- 알고리즘
- BindingAdapter
- disjoint set
- springdoc
- kapt
- SCC
- 분리 집합
- MySQL
- 그래프
- MST
- Today
- Total
기맹기 개발 블로그
[BOJ 26157] 즉흥 여행 (Hard) (Java) 본문
BOJ 26157 즉흥 여행 (Hard)
난이도 : 플래 3
https://www.acmicpc.net/problem/26157
26157번: 즉흥 여행 (Hard)
첫째 줄에 나라의 개수 $ N $과 항공편의 개수 $ M $이 주어진다. $( 1 \le N \le 200\,000; $ $ 0 \le M \le 500\,000 )$ 둘째 줄부터 $ M $개의 줄에 걸쳐 항공편의 정보가 두 정수 $ v $ $ w $로 주어진다. 이는 $ v $
www.acmicpc.net
전략
SCC에 속한 임의의 두 정점은 방문할 수 있다.
위의 예시에서는 그래프 전체가 하나의 SCC이므로 모든 정점이 결과가 된다.
위의 예시에서는 SCC는 [1], [2, 3, 4] 두 개이며 SCC로 그래프를 압축하면 [1] -> [2, 3, 4] 이다.
따라서 [1]의 정점들이 결과가 된다.
즉, 압축한 그래프에서 모든 SCC를 방문할 수 있는 첫번째 SCC를 찾아야하므로 위상정렬을 사용할 수 있다.
아래의 예시에서는 결과가 없다.
위 예시에서는 [1, 2], [3, 4]가 SCC가 되며 압축된 그래프에서 두 SCC 사이에 간선이 없으므로 결과는 없다.
위상정렬 시 진입차수가 0인 SCC가 [1], [5] 두 개이므로 하나의 SCC를 선택하면 나머지를 방문할 수 없다.
만약 [5]를 선택하였다면 [2, 3, 4]를 방문한 후 [1]을 방문하여야 모든 정점을 탐색할 수 있는데,
[1] -> [2, 3, 4] 이고 [2, 3, 4] -> [1] 이면 SCC에 위배된다. 즉 [1, 2, 3, 4]여야 하기 때문이다.
따라서 SCC로 압축한 그래프에서 위상정렬하면서 큐의 크기가 2 이상인 경우에는 결과가 없으며,
그렇지 않은 경우 진입차수가 0인 첫번째 SCC의 정점들을 출력하도록 하면 된다.
위상정렬 시 큐가 빈 경우는 생기지 않는다. SCC 압축 그래프에서 DAG가 보장되기 때문이다.
코드
package BOJ_26157;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static List<Integer>[] adj;
static int[] sccId, discovered;
static Stack<Integer> stack;
static int sccCounter, vertexCounter;
static List<SCC> sccList;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n, m;
String[] inputs = br.readLine().split(" ");
n = Integer.parseInt(inputs[0]);
m = Integer.parseInt(inputs[1]);
adj = new ArrayList[n + 1];
discovered = new int[n + 1];
sccId = new int[n + 1];
sccList = new ArrayList<>();
for (int i = 1; i < n + 1; i++) {
adj[i] = new ArrayList<>();
discovered[i] = -1;
sccId[i] = -1;
}
for (int i = 0; i < m; i++) {
inputs = br.readLine().split(" ");
int from = Integer.parseInt(inputs[0]);
int to = Integer.parseInt(inputs[1]);
adj[from].add(to);
}
stack = new Stack<>();
for (int i = 1; i < n + 1; i++) {
if (discovered[i] == -1) {
scc(i);
}
}
compressDAG();
SCC first = topologySort();
if (first == null) {
System.out.println(0);
return;
}
Collections.sort(first.vertexes);
StringBuilder sb = new StringBuilder();
sb.append(first.vertexes.size() + "\n");
for (int vertex : first.vertexes) {
sb.append(vertex + " ");
}
System.out.println(sb);
}
// 그래프를 SCC 단위로 압축
static void compressDAG() {
for (SCC scc : sccList) {
for (int vertex : scc.vertexes) {
for (int next : adj[vertex]) {
if (scc.id != sccId[next]) {
SCC linkScc = sccList.get(sccId[next]);
scc.link.add(linkScc);
linkScc.inDegree++;
}
}
}
}
}
// SCC 들을 위상절렬 한 후 첫번쨰 SCC 를 반환
// 큐에 동시에 두 개 이상의 SCC 가 존재하거나 위상정렬이 불가하면 NULL 반환
static SCC topologySort() {
Queue<SCC> queue = new LinkedList<>();
List<SCC> result = new ArrayList<>();
for (SCC scc : sccList) {
if (scc.inDegree == 0) {
queue.offer(scc);
}
}
for (int i = 0; i < sccList.size(); i++) {
if (queue.isEmpty())
return null;
if (queue.size() > 1)
return null;
SCC cur = queue.poll();
for (SCC next : cur.link) {
if (--next.inDegree == 0) {
queue.offer(next);
}
}
result.add(cur);
}
return result.get(0);
}
static int scc(int here) {
int ret = discovered[here] = vertexCounter++;
stack.push(here);
for (int there : adj[here]) {
if (discovered[there] == -1) {
ret = Math.min(ret, scc(there));
}
else if (sccId[there] == -1) {
ret = Math.min(ret, discovered[there]);
}
}
if (ret == discovered[here]) {
SCC scc = new SCC(sccCounter);
while (true) {
int t = stack.pop();
sccId[t] = sccCounter;
scc.vertexes.add(t);
if (t == here) break;
}
sccList.add(scc);
sccCounter++;
}
return ret;
}
static class SCC {
final int id;
final List<Integer> vertexes = new ArrayList<>();
final List<SCC> link = new ArrayList<>();
int inDegree = 0;
public SCC(int id) {
this.id = id;
}
}
}
깨달은 점
SCC를 응용한 문제를 풀어보면서 어떤 상황에서 사용하는지 조금씩 감을 잡기 시작했다.
'PS' 카테고리의 다른 글
[BOJ 16946] 벽 부수고 이동하기 4 (Java) (0) | 2022.12.29 |
---|---|
[BOJ 17143] 낚시왕 (Java) (0) | 2022.12.29 |
[BOJ 2150] Strongly Connected Component (Java) (0) | 2022.12.27 |
[BOJ 2473] 세 용액 (Java) (1) | 2022.12.27 |
[BOJ 12100] 2048 (Easy) (Java) (0) | 2022.12.26 |