Introduction

단절점이란 제거했을때 연결되어있던 그래프가 둘 이상으로 나눠지는 그래프를 말한다.

 

단절점을 가장 naive 하게 구하는 방법은 모든 정점에 대해 그 정점을 제거할 경우 connected component가 증가하는지를 탐색을 통해 확인하면 된다. 이 경우 시간복잡도는 $O(V^2)$ 가 된다.

 

하지만 단절점의 특징을 사용해서 탐색 한번으로 $O(V)$ 단절점을 구할 수 있다.

 

단절점 구하기

아래 그림을 보자.

위 그림은 root를 시작점으로 dfs를 도는 중간 과정을 나타낸다.

 

빨간색 정점 : 현재 보고있는 정점이다.

검은색 간선 : 바로 직전에 타고(?) 온 간선으로 root 쪽에서 왔다는것을 나타낸다.

보라색 정점 : root 쪽에서 이미 탐색을 완료한 정점이다.

파란색 덩어리(?) : 아직 방문하지 않은 connected component로, 빨간색의 dfs number보다 더 작은 dfs number을 가진 정점과는 절대! 연결되어 있지 않아야 된다. (빨간색 정점을 제거해도 파란색 덩어리 내에서 서로 연결되어 있다.)

 

여기서 dfs number들을 비교해 보면 (root 정점, 보라색 정점) < (빨간색 정점) < (파란색 덩어리들) 인 것을 알 수 있다.

 

그럼 빨간색 정점이 단절점이 될 조건을 살펴보면, 파란색 덩어리가 한개 이상 있으면 단절점 이라는 것을 알 수 있다.

 

특정 점에서 dfs를 돌고 난 후 리턴 값을 '그 정점에서 탐색을 돌았을때 만나는 가장 작은 dfs number' 로 정의하자.

그러면 파란색 덩어리를 구별하는 방법은, 탐색의 리턴값이 현재 자신의 dfs number보다 같거나 클 경우 이다.

 

다만, 이러한 방법으로는 root의 단절점 여부는 판단할 수 없다.

root 정점의 단절점 여부는, root 정점에서 탐색을 돈 횟수가 2 이상이면 단절점으로 판단한다.

이는 조금만 생각해보면 당연함을 알 수 있다.

 

코드

코드는 이 문제를 기준으로 작성되었다.

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int v, e, num[10001];
bool cutable[10001] = { false };
vector<int> adj[10001];

int dfsNum;
int dfs(int idx, int pre) {

	num[idx] = dfsNum++;

	int minTime = num[idx], maxTime = -1, cnt = 0;

	for (int i = 0; i < adj[idx].size(); i++) {
		int nex = adj[idx][i];
		if (nex == pre)	continue;
		
		int dfsTime;

		if (num[nex] == 0) {
			dfsTime = dfs(nex, idx);
			maxTime = max(maxTime, dfsTime);
			cnt++;
		}
		else
			dfsTime = num[nex];

		minTime = min(minTime, dfsTime);
	}

	if (pre != -1)
		cutable[idx] = maxTime >= num[idx];
	else
		cutable[idx] = cnt >= 2;

	return minTime;
}

int main(void) {
	scanf("%d %d", &v, &e);

	for (int i = 0; i < e; i++) {
		int a, b;
		scanf("%d %d", &a, &b);

		adj[a].push_back(b);
		adj[b].push_back(a);
	}

	for (int i = 1; i <= v; i++) {
		if (num[i] == 0) {
			dfsNum = 1;
			dfs(i, -1);
		}
	}

	int pointCnt = 0;
	for (int i = 1; i <= v; i++)
		pointCnt += (int)cutable[i];

	printf("%d\n", pointCnt);
	for (int i = 1; i <= v; i++) {
		if (cutable[i])
			printf("%d ", i);
	}
	printf("\n");

	return 0;
}

'PS' 카테고리의 다른 글

Mo's Algorithm  (0) 2023.08.14
Sqrt Decomposition  (0) 2023.08.13
Master Theore  (0) 2023.08.11
Euler tour technique  (0) 2023.08.08
monotone queue  (0) 2023.08.05
zanzun