4. Background : DFS Spanning Tree
DFS Spanning Tree :
DFS 를 수행하면서 생성된 Spanning Tree 를 의미
1
2
4
3
5
6
5. DFS Spanning Tree 의 각 정점은
3 가지의 상태를 가진다.
● EXPLORED : 방문했지만 DFS 를 끝내지 않은 상태
● UNVISITED : 방문하지 않은 상태
● VISITED : 방문 후 DFS 를 끝낸 상태
6. Terminology : Tree Edge
DFS 를 통해 탐색 중인 간선.
EXPLORED 인 정점에서 UNVISITED 인 정점으로 향하는 간선
0
1 3
2
7. Terminology : Back Edge
사이클의 일부에 해당하는 간선.
EXPLORED 상태인 정점에서 EXPLORED 상태인 정점으로 향하는 간선
8. Terminology : Forward/Cross Edge
EXPLORED 상태인 정점에서 VISITED 상태인 정점으로 향하는 간선
DFS 수행 순서에 따라 Forward Edge/Tree Edge 가 뒤바뀔 수도 있다.
(Forward/Cross edge 여부는 상대적인 개념으로 이해하는게 좋다.)
13. #4 Strongly Connected Components
● C(v) = { u ∈ V | there exists a path in G from u to v and a path
in G from v to u }.
Directed Graph 에서 도달할 수 있는 놈들끼리 분류한다.
REF.
16. SCC 알고리즘을 구현하는 두가지 알고리즘
● Kosaraju algorithm
○ DFS 를 두번 돌리는 알고리즘
○ 정방향 그래프와 역방향 그래프가 같은 SCC를 가진다는 성질을 이용함
○ O(V + E)
● Tarjan algorithm
○ O(V + E)
둘 중 어떤 알고리즘을 써도 상관 없다.
REF.
17. #4 Strongly Connected Components
Void KosarajuSCC(int u) {
1. 정방향 그래프를 위상정렬 후 스택에 저장
(순회를 끝낸 노드를 스택에 push 하는 방식으로 DFS)
2. 스택이 빌 때까지 최상위 원소부터 하나씩 빼가면서
(방문하지 않았을 경우에만) 역방향 그래프에서 DFS를 수행
2-1. 위에서 수행하는 DFS에서 탐색되는 모든 노드를 SCC로 묶는다.
}
REF.
24. Naive 한 방법(Articulation Point)
1. O(V+E) DFS 를 수행하여 원래 그래프에 대한 Connected Component 의 갯수를
구한다.
2. 각 정점 v ∈ V 에 대해
a. 정점 v 및 v 에 인접한 간선들을 절단한다.
b. O(V + E) DFS 를 수행하여 Connected Component 의 갯수가 증가하는지 확인한다.
c. 그렇다면, v 는 절단점이다. 절단하기 전의 상태로 다시 복구
25. Naive 한 방법
1. O(V+E) DFS 를 수행하여 원래 그래프에 대한 Connected Component 의 갯수를
구한다.
2. 각 정점 v ∈ V 에 대해
a. 정점 v 및 v 에 인접한 간선들을 절단한다.
b. O(V + E) DFS 를 수행하여 Connected Component 의 갯수가 증가하는지 확인한다.
c. 그렇다면, v 는 절단점이다. 절단하기 전의 상태로 다시 복구
26. O(V+E) 안에 해결되는 알고리즘도 존재한다.
Cycle Detection 을 수행하는 것과 유사하게,
O(V+E) DFS 를 수행하여 DFS Spanning Tree 를 구축하는 과정에서
단절점을 빠르고 효율적으로 찾아낼 수 있다.
27. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0
3
1
4
2
5
28. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0
3
1
4
2
5
29. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0
3
1
4
2
5
30. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0
3
1
4
2
5
DFS Spanning Tree 를 구축한 시점에서
현재 정점 u 에서 다음 정점 v 를 보았을 때
dfs_low(v) >= dfs_num(u) 성질이 성립하면
단절점이다.
31. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0
3
1
4
2
5
DFS Spanning Tree 를 구축한 시점에서
현재 정점 u 에서 다음 정점 v 를 보았을 때
dfs_low(v) >= dfs_num(u) 성질이 성립하면
단절점이다.
33. 예외 케이스
0
3
1
4
2
5
그렇게 따지면 루트도
단절점이 되어야 하는 것은
아닌가?
DFS Spanning Tree 의 루트가 둘
이상의 자식을 가질 경우 단절점이 될
수 있음. 하나인 경우는 해당되지
않는다.
34. Bridge
단절선(Bridge)
: 그래프 G 의 간선 중에서 이를 제거했을 때,
G 가 연결되지 않도록 만드는 간선.
(즉, 정점을 제거했을 때 Connected Component 가 증가하도록 하는 간선)
● 단절점과 거의 유사
35. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0
3
1
4
2
5
현재 정점 u 에서 다음 정점 v 를 보았을 때
dfs_low(v) > dfs_num(u) 성질이 성립하면
단절점이다.
41. Overview
● dfs_num(u) : u를 맨 처음 방문했을 때 몇 번째 방문한 정점인지 기록
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : 정점 u에 대한 DFS spanning tree 에서 도달가능한 dfs_num 최솟값
0 1
2
3 4
6
5
7
42. Overview
● dfs_num(u) : 몇 번째로 방문한 정점인지 기록하기 위한 용도
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : DFS 의 시작이 되는 정점
0 1
2
3 4
6
5
7
43. Overview
● dfs_num(u) : 몇 번째로 방문한 정점인지 기록하기 위한 용도
○ UNVISITED 를 EXPLORED, VISITED 와 구별하는 것 이상의 역할
● dfs_low(u) : DFS 의 시작이 되는 정점
0 1
2
3 4
6
5
7
44. Tarjan’s SCC Algorithm (Simplified)
1. DFS Spanning Tree 를 구축하면서, 방문한 순서를 스택에 저장.
2. DFS Spanning Tree 를 구축해놓은 시점에서,
dfs_low(u) == dfs_num(u) 일 때,
각 SCC 의 루트에 해당되는 정점 u 를 뽑을 때까지 스택에서 pop 하여 SCC
리스트에 추가
3. 모든 정점을 모두 방문할때까지 1,2 과 같이 DFS 를 수행
45.
46.
47. SCC를 이용하여 푸는 문제들
https://www.acmicpc.net/problem/10451 : 간단하게 SCC의 갯수 출력
https://www.acmicpc.net/problem/2150 : 출력이 까다로운 문제(SCC의 구성요소
출력)
50. 2-SAT Problem
Implication Graph 를 구축하는 것이 핵심.
Implication :
p 이면 q 이다
⇔ if q, then q
⇔ p implies q
⇔ p => q
⇔ ~p V q
p q p => q
false false true
false true true
true false false
true true true
52. Background : implication
A V B
⇔ ~A => B
⇔ ~B => A
A
⇔ A ⋀ A
A V B
⇔ ~A => B
⇔ ~B => A
⇔ ( A V B ) ⋀ ( A V B )
⇔ ( ~A => B ) ⋀ ( ~B => A )
53. Implication Graph
A V B
⇔ ~A => B
⇔ ~B => A
⇔ ( A V B ) ⋀ ( A V B )
⇔ ( ~A => B ) ⋀ ( ~B => A )
A
~A
B
~B
54. (~A V B ) ⋀ (~B V C) ⋀ (A V C) ⋀ (C V B)
~A
~B
~C
A
B
C
55. 어떻게 2-CNF 식이 참이 될 수 있는 것을
증명하는가?
p q p => q
false false true
false true true
true false false
true true true
p=>q 의 식을 통해
간선으로 추가한 함의 그래프에서
p 가 참이면 q 도 역시 참이어야 하고
논리변수 p와 같은 SCC 에 포함된
모든 논리식은 참 이 됨.
모든 SCC 에 포함된 논리식이 전부
참이 될 수 있다면, 2-CNF 식은 참이
된다.
56. P => Q ⋀ Q => R
⇔ P => R
P => Q ⋀ Q => R ⋀ R => P
⇔ P => P ⇔ ~P V P ⇔ True
P Q
R
57. 어떻게 2-CNF 의 식이 거짓이
될 수 있는 것을 증명하는가?
p q ~p p => ~p
false false true true
false true true true
true false false false
true true false false
58. 어떻게 2-CNF 의 식이 거짓이
될 수 있는 것을 증명하는가?
~p p p => ~p ~p => p
true false true false
true false true false
false true false true
false true false true
59. 어떻게 2-CNF 의 식이 거짓이
될 수 있는 것을 증명하는가?
~p p p => ~p ~p => p p => ~p && ~p => p
true false true false false
true false true false false
false true false true false
false true false true false
A ⋀ ~A ⇔ False
(contradiction)
60. 어떻게 2-CNF 의 식이 거짓이
될 수 있는 것을 증명하는가?
~p p p => ~p ~p => p p => ~p && ~p => p
true false true false false
true false true false false
false true false true false
false true false true false
A ⋀ ~A ⇔ False
(contradiction)
추이적인 닫힘(Transitive closure) 을 통해
Xi 와 ~Xi 가 같은 SCC 에서 도달할 수
있으면,
2-CNF 식이 거짓임을 증명할 수 있다.
(단, Xi ∈ {X1, X2, …, Xn} )
61. 다시 CNF 식으로 돌아와서 살펴보자.
위의 논리식은 Tautology 를 이용하여 다른 식으로 재해석할 수 있고,
Implication graph 에서 어떤 SCC 로 각각 묶이느냐에 따라
(교환법칙/결합법칙에 따라) 논리곱을 임의의 순서로 적용할 수 있다.
논리곱의 성질에 의해, 위의 논리식이 참이 되려면
SCC 에 포함된 모든 논리식이 참이 되어야 하고,
Transitive closure 를 통해 어떤 식이라도 거짓이 된다면 위의 논리식은 거짓이 된다.