BOJ 백준 4196 : 도미노
Updated:
시작하면서
이번 문제는 강한 연결 요소(SCC: Strongly Connected Component) 를 응용하는 문제이다.
SCC 개념은 해당 블로그를 추천한다. LINK
문제 이해
이 문제는 일반적인 도미노 개념을 생각하면 된다. 최소로 도미노를 넘어뜨리기 위해서는 가장 앞, 그래프로 쉽게 말하면 위상 정렬 된 그래프에서 가장 최상위 노드를 넘어뜨리면 된다.
이렇게 생각했을 때는 굳이 SCC까지 구하지 않고, Out Degree가 0 인 개수만 찾으면 될거 같지만, 사이클이 생기는 경우가 있기 때문에 해당 방법으로는 문제가 해결되지 않는다. (내가 이걸 잊고 30분이나 삽질했다)
문제 해결 방법은 다음과 같다.
- 문제에서 주어진 도미노 블록의 관계를 바탕으로 SCC를 만든다.
- 만들어진 각 SCC 그룹끼리 Out Degree 가 있는지 확인한다.
- Out Degree가 하나도 존재하지 않는 SCC 그룹의 개수가 문제의 답이다.
해결
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#include <math.h>
using namespace std;
class StronglyConnectedComponent
{
public:
vector<vector<int> > graph;
vector<vector<int> > scc;
vector<int> dfsNumber;
vector<int> sccNumber;
vector<bool> finished;
stack<int> visitedRoute;
int currentDfsCount;
int currentSccNumber;
StronglyConnectedComponent(int size)
{
graph.resize(size + 1);
scc.resize(size + 1);
dfsNumber.resize(size + 1);
sccNumber.resize(size + 1);
finished.resize(size + 1);
currentDfsCount = 0;
currentSccNumber = 0;
}
};
int MakeStronglyConnectedComponent(StronglyConnectedComponent &scc, int currentNumber)
{
int result = 0;
// 1. 현재 노드의 dfs number를 대입해주고, 방문 기록 스택에 푸시한다.
scc.dfsNumber[currentNumber] = ++scc.currentDfsCount;
scc.visitedRoute.push(currentNumber);
// 2. 현재 노드의 dfs number와 연결된 노드들의 dfs number 중 가장 작은 번호를 result로 사용한다.
result = scc.dfsNumber[currentNumber];
for (int i = 0; i < scc.graph[currentNumber].size(); i++)
{
int nextNumber = scc.graph[currentNumber][i];
if (scc.dfsNumber[nextNumber] == 0)
{
result = min(result, MakeStronglyConnectedComponent(scc, nextNumber));
}
else if (scc.finished[nextNumber] == false)
{
result = min(result, scc.dfsNumber[nextNumber]);
}
}
// 3. 현재 노드와 연결된 노드들 중 제일 높은 정점이 현재 노드일 경우, scc를 만든다.
if (result == scc.dfsNumber[currentNumber])
{
vector<int> currentScc;
while (true)
{
int topNumber = scc.visitedRoute.top();
scc.visitedRoute.pop();
currentScc.push_back(topNumber);
scc.finished[topNumber] = true;
scc.sccNumber[topNumber] = scc.currentSccNumber;
if (topNumber == currentNumber)
{
break;
}
}
// 3-1. scc를 추가한다.
sort(currentScc.begin(), currentScc.end());
scc.scc.push_back(currentScc);
scc.currentSccNumber++;
}
return result;
}
int main()
{
// sync off
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// sync off
int T, N, M;
cin >> T;
while (T--)
{
cin >> N >> M;
StronglyConnectedComponent scc(N);
for (int i = 0; i < M; i++)
{
int node01, node02;
cin >> node01 >> node02;
scc.graph[node01].push_back(node02);
}
// 1. SCC 만들기
for (int i = 1; i < N + 1; i++)
{
if (scc.dfsNumber[i] != 0)
{
continue;
}
MakeStronglyConnectedComponent(scc, i);
}
// 2. 다른 SCC 그룹끼리의 간선이 있는지 체크
vector<int> sccOutDegree(scc.currentSccNumber);
for (int i = 1; i < N + 1; i++)
{
for (int j = 0; j < scc.graph[i].size(); j++)
{
if (scc.sccNumber[i] == scc.sccNumber[scc.graph[i][j]])
{
continue;
}
sccOutDegree[scc.sccNumber[scc.graph[i][j]]]++;
}
}
// 3. 다른 SCC에서 들어오는 간선이 없는 SCC의 개수가 답
int result = 0;
for (int i = 0; i < sccOutDegree.size(); i++)
{
if (sccOutDegree[i] == 0)
{
result++;
}
}
cout << result << "\n";
};
return 0;
}
Leave a comment