본문 바로가기
ps

[백준] 4196 - 도미노

by kariskan 2022. 7. 14.

https://www.acmicpc.net/problem/4196

 

4196번: 도미노

도미노는 재밌다. 도미노 블록을 일렬로 길게 늘어세운 뒤 블록 하나를 넘어뜨리면 그 블록이 넘어지며 다음 블록을 넘어뜨리는 일이 반복되어 일렬로 늘어선 블록들을 연쇄적으로 모두 쓰러

www.acmicpc.net

 

SCC를 연습하고 있던 중에 발견한 흥미로운 문제이다.

 

도미노의 특성상 하나의 도미노를 쓰러트리면 다른 도미노도 쓰러질 수 있기 때문에 SCC를 찾아야 한다.

그런데 여기서 끝나는 것이 아니다. 바로 하나의 SCC는 다른 SCC에 영향을 줄 수 있다.

 

만약

1 -> 2, 2 -> 3, 3 -> 1, 4 -> 3, 5 -> 4

같은 경우에서 

SCC는 {1, 2, 3}, {4}, {5}이지만 5번 도미노 하나만 쓰러트리면 모든 도미노를 쓰러트릴 수 있다.

 

이 사실을 알고 위상 정렬을 생각했다. 하지만 일반적인 정점 간의 위상 정렬은 사이클이 있는 그래프에서는 성립되지 못한다 => SCC 간의 위상 정렬을 사용한다.

 

즉, {1, 2, 3}, {4}, {5}를 각각 SCC1, SCC2, SCC3이라 하고 위상 정렬을 수행하면

{5} -> {4} -> {1, 2, 3} 이 되어서 indegree = 0 인 5번 도미노 하나만 쓰러트리면 된다는 사실을 알 수 있다.

 

따라서 해당 문제에서는 각 SCC마다 idx를 매겨주고, SCC간의 연결을 탐색해 indegree = 0인 도미노의 개수가 최종 답이 된다.

 

결론적으로 SCC + 위상 정렬 문제였다.

 

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int n, m;
vector<vector<int>> v;
vector<int> s;
int visit[100001], scc[100001], inp[100001];
int id = 1, ans, sccIdx = 1;

int dfs(int idx) {
	int ret = visit[idx] = id++;
	s.push_back(idx);
	for (auto i : v[idx]) {
		if (!visit[i]) {
			ret = min(ret, dfs(i));
		}
		else if (!scc[i]) {
			ret = min(ret, visit[i]);
		}
	}

	if (ret == visit[idx]) {
		while (1) {
			int t = s.back();
			scc[t] = sccIdx;
			s.pop_back();
			if (t == idx)break;
		}
		sccIdx++;
	}
	return ret;
}

int main() {
	int tc;
	cin >> tc;
	while (tc--) {
		cin >> n >> m;
		v.resize(n + 1);
		for (int i = 0; i < m; i++) {
			int a, b; cin >> a >> b;
			v[a].push_back(b);
		}
		for (int i = 1; i <= n; i++) {
			if (!scc[i])dfs(i);
		}
		for (int i = 1; i <= n; i++) {
			for (auto j : v[i]) {
				if (scc[i] != scc[j]) {
					inp[scc[j]]++;
				}
			}
		}
		for (int i = 1; i < sccIdx; i++) {
			if (inp[i] == 0)ans++;
		}
		cout << ans << '\n';

		memset(visit, 0, sizeof(visit));
		memset(scc, 0, sizeof(scc));
		memset(inp, 0, sizeof(inp));
		id = 1; sccIdx = 1; ans = 0;
		v.clear(); s.clear();
	}
	return 0;
}

'ps' 카테고리의 다른 글

[백준] 1854 - K번째 최단경로 찾기  (0) 2022.07.19
[백준] 13904 - 과제  (0) 2022.07.19
[백준] 11003 - 최솟값 찾기  (0) 2022.07.14
[백준]1525 - 퍼즐  (0) 2022.07.13
[백준]1766 - 문제집  (0) 2022.07.13

댓글