반응형
정의
방향 그래프에서, 모든 정점들이 서로 도달 가능한 최대 부분 그래프
⇒ SCC 내 임의의 정점 $u,v$에 대해, $u \rightarrow v$경로와 $v \rightarrow u$경로가 모두 존재
특성
- 모든 방향 그래프는 하나 이상의 SCC로 분해될 수 있음
- SCC들은 서로 겹치지 않음 (SCC 정의가 최대 부분 그래프이기 때문)
- 모든 정점은 정확히 하나의 SCC에 속함
- 방향 그래프의 SCC들을 하나의 정점으로 압축하면 DAG가 됨.
SCC 찾기
코사라주 알고리즘
- 각 정점에 DFS Finishing Time을 기록
- Finishing Time ⇒ 오일러 경로 테크닉에서 out time의 개념과 동일함
- 모든 간선의 방향을 뒤집은 전치 그래프에서, 탐색 종료의 역순으로 정점을 방문하여 생긴 DFS 트리가 하나의 SCC가 됨
구현해보자.
- 정방향, 역방향 그래프 $G, R$을 준비한다.
- Finishing Time의 역순을 구하는 대신, 탐색을 마칠 때 Stack에 정점을 넣어주는 방식을 채택
- Stack에서 하나씩 꺼내며 SCC 추출
타잔 알고리즘
- 각 정점에 DFS Discovery Time과 low-link값을 기록low-link ⇒ 도달 가능한 정점의 Discovery Time 중 최솟값
- Discovery Time ⇒ 오일러 경로 테크닉에서 in time의 개념과 동일함
코드
https://www.acmicpc.net/problem/2150 AC 코드
import java.util.*;
import java.io.*;
public class Main {
// IO field
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
static StringTokenizer st = new StringTokenizer("");
static void nextLine() throws Exception {st = new StringTokenizer(br.readLine());}
static String nextToken() throws Exception {
while(!st.hasMoreTokens()) nextLine();
return st.nextToken();
}
static int nextInt() throws Exception { return Integer.parseInt(nextToken()); }
static long nextLong() throws Exception { return Long.parseLong(nextToken()); }
static double nextDouble() throws Exception { return Double.parseDouble(nextToken()); }
static void bwEnd() throws Exception {bw.flush();bw.close();}
// Additional field
static int V, E;
static List<Integer>[] G, R;
static Stack<Integer> S;
static boolean[] vis;
static int ord = 0;
public static void main(String[] args) throws Exception {
ready();
solve();
bwEnd();
}
static void ready() throws Exception{
V = nextInt();
E = nextInt();
G = new List[V+1];
R = new List[V+1];
S = new Stack<>();
vis = new boolean[V+1];
for(int i=1;i<=V;i++) {
G[i] = new ArrayList<>();
R[i] = new ArrayList<>();
}
for(int i=0;i<E;i++) {
int a = nextInt(), b = nextInt();
G[a].add(b);
R[b].add(a);
}
}
static void solve() throws Exception {
for(int i=1;i<=V;i++) if(!vis[i]) {
vis[i] = true;
dfs1(i);
}
vis = new boolean[V+1];
List<List<Integer>> ans = new ArrayList<>();
while(!S.isEmpty()) {
int n = S.pop();
if(vis[n]) continue;
vis[n] = true;
List<Integer> res = new ArrayList<>();
dfs2(n, res);
Collections.sort(res);
ans.add(res);
}
Collections.sort(ans, (a,b) -> a.get(0).compareTo(b.get(0)));
bw.write(ans.size() + "\n");
for(List<Integer> res:ans) {
for(int i:res) bw.write(i + " ");
bw.write("-1\n");
}
}
static void dfs1(int n) {
ord++;
for(int i:G[n]) if(!vis[i]) {
vis[i] = true;
dfs1(i);
}
S.add(n);
}
static void dfs2(int n, List<Integer> L) {
L.add(n);
for(int i:R[n]) if(!vis[i]) {
vis[i] = true;
dfs2(i, L);
}
}
}반응형
'알고리즘' 카테고리의 다른 글
| SPFA (Shortest Path Faster Algorithm) (0) | 2025.12.05 |
|---|---|
| 제곱근 분할법 (Square Root Decomposition) (5) | 2025.08.03 |
| Li Chao Tree (0) | 2024.10.31 |