PS❯BOJ2021-10-313 min read
[BOJ 11400] 단절선 - Java (DFS Spanning Tree & Tarjan 알고리즘)
무향 연결 그래프에서 DFS 스패닝 트리를 순회하며 자식 서브트리가 조상 노드로 우회할 수 없는 간선 ($low[v] > order[u]$)을 찾는 단절선 알고리즘
#BOJ#단절선#그래프#DFS#Tarjan#Java
1. 문제 개요 및 조건 분석
| 항목 | 내용 |
|---|---|
| 문제 이름 | 단절선 (백준 11400번 / 플래티넘 1) |
| 플랫폼 / 난이도 | 백준 (플래티넘 1, 11400번) |
| 문제 링크 | 백준 11400번 |
| 제한 사항 | 시간 제한 1초, 메모리 제한 256MB |
| 입력 조건 | 정점 수 $V \le 100,000$, 간선 수 $E \le 1,000,000$ (무향 연결 그래프) |
| 핵심 규칙 | 그래프에서 그 간선을 제거했을 때 그래프가 2개 이상으로 분리되는 **모든 단절선(Bridge)**을 찾아 오름차순 출력 |
2. 접근 아이디어: DFS Spanning Tree & Tarjan 단절선 판별
단절선(Articulation Bridge) 판별 조건
- 무향 연결 그래프에서 임의의 정점에서 DFS를 수행하면 DFS 스패닝 트리와 **역방향 간선(Back edge)**으로 분리됩니다.
- 트리 간선 $(u, v)$ (여기서 $u$가 $v$의 부모)에 대해, $v$와 $v$의 서브트리 노드들이 역방향 간선을 타고 올라갈 수 있는 가장 높은 조상의 방문 순서를
low[v]라고 합시다. - 만약: $$low[v] > order[u]$$ 라면, $v$의 서브트리에서는 간선 $(u, v)$를 우회하여 $u$나 그 조상 노드로 도달할 수 있는 우회로가 전혀 없다는 뜻입니다.
- 따라서 간선 $(u, v)$는 제거 시 그래프가 분리되는 **단절선(Bridge)**이 됩니다!
3. Java 풀이 코드
import java.io.*;
import java.util.*;
public class Main {
static class Edge implements Comparable<Edge> {
int u, v;
Edge(int u, int v) {
this.u = Math.min(u, v);
this.v = Math.max(u, v);
}
@Override
public int compareTo(Edge o) {
if (this.u == o.u) return this.v - o.v;
return this.u - o.u;
}
}
static int v, e;
static List<Integer>[] adj;
static int[] order;
static int timer = 1;
static List<Edge> bridges = new ArrayList<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
v = Integer.parseInt(st.nextToken());
e = Integer.parseInt(st.nextToken());
adj = new ArrayList[v + 1];
for (int i = 1; i <= v; i++) adj[i] = new ArrayList<>();
for (int i = 0; i < e; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
adj[a].add(b);
adj[b].add(a);
}
order = new int[v + 1];
for (int i = 1; i <= v; i++) {
if (order[i] == 0) {
dfs(i, 0);
}
}
Collections.sort(bridges);
StringBuilder sb = new StringBuilder();
sb.append(bridges.size()).append("\n");
for (Edge edge : bridges) {
sb.append(edge.u).append(" ").append(edge.v).append("\n");
}
System.out.print(sb);
}
static int dfs(int u, int parent) {
order[u] = timer++;
int low = order[u];
for (int next : adj[u]) {
if (next == parent) continue; // 부모로 바로 돌아가는 간선 제외
if (order[next] > 0) {
// 이미 방문한 노드 (Back edge)
low = Math.min(low, order[next]);
} else {
// 트리 간선
int childLow = dfs(next, u);
// 단절선 조건: 자식이 u의 조상으로 우회할 수 없음
if (childLow > order[u]) {
bridges.add(new Edge(u, next));
}
low = Math.min(low, childLow);
}
}
return low;
}
}
4. 복잡도 분석
- 시간 복잡도: $O(V + E)$ — DFS 1회 순회로 모든 단절선을 $O(V + E)$에 판별 + 결과 정렬 $O(K \log K)$. $V = 100,000, E = 1,000,000$ 규모에서 약 0.7초 이내에 1초 제한 내 통과.
- 공간 복잡도: $O(V + E)$ — 인접 리스트 및 방문 순서 배열.
5. 채점 결과 및 통과 인증

← PREVIOUS LOG
[BOJ 11266] 단절점 - Java (DFS Spanning Tree & Tarjan 알고리즘)
NEXT LOG →[BOJ 1461] 도서관 - Java (그리디 정렬 & 양수/음수 분할 탐색)
COMMENTS // GITHUB DISCUSSIONS (GISCUS)