먼저 모든 도미노의 연결 관계를 저장한다. (자신을 넘어뜨릴 수 있는 도미노는 부모, 자신이 넘어뜨리는 도미노는 자식으로 설정하여 연관관계를 저장)
부모가 없는 도미노의 경우 사람이 직접 넘어뜨려야 하므로 해당 도미노를 시작으로 연결된 도미노를 전부 넘어뜨린다. (방문 체크 배열로 넘어진 도미노 표시)
남은 도미노 중 자식이 2개 존재하는 배열을 찾는다. (올가미 형태의 도미노) 그리고 해당 도미노를 넘어뜨리면 연결되어 있는 모든 도미노가 넘어진다.
남은 도미노 중 넘어뜨리지 않은 도미노가 있을 경우 넘어뜨린다. (루프 형태의 도미노) 루프 형태는 아무 도미노를 넘어뜨려도 연결된 도미노 모두 넘어진다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
StringTokenizer st;
int T = Integer.parseInt(br.readLine()); // 테스트 케이스 수 입력
// 테스트 케이스 수 만큼 반복
for(int t = 0; t < T; t++) {
st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken()); // 노드 수
int m = Integer.parseInt(st.nextToken()); // 간선 수
int[] parent = new int[n+1]; // 각 노드의 부모 수를 저장하는 배열
int[] decendent = new int[n+1]; // 각 노드의 자식 수를 저장하는 배열
boolean[] visited = new boolean[n+1]; // 방문 여부를 저장하는 배열
ArrayList<Integer>[] al = new ArrayList[n+1]; // 인접 리스트
for(int i = 0; i <= n; i++)
al[i] = new ArrayList<>(); // 인접 리스트 초기화
// 간선 정보 입력 및 인접 리스트 구성
for(int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
parent[b]++; // b의 부모 노드 개수 증가
decendent[a]++; // a의 자식 노드 개수 증가
al[a].add(b); // a에서 b로 가는 간선 추가
}
int count = 0; // 제거할 노드 그룹 수를 카운트
// 부모가 없는 노드 그룹 제거
for(int i = 1; i <= n; i++) {
if(!visited[i] && parent[i] == 0) {
count++;
bfs(i, visited, al);
}
}
// 자식이 2개 이상인 노드 그룹 제거
for(int i = 1; i <= n; i++) {
if(!visited[i] && decendent[i] > 1) {
count++;
bfs(i, visited, al);
}
}
// 루프가 있는 노드 그룹 제거 (자식이 1개인 노드)
for(int i = 1; i <= n; i++) {
if(!visited[i] && decendent[i] == 1) {
count++;
bfs(i, visited, al);
}
}
sb.append(count).append("\n"); // 테스트 케이스별 결과 추가
}
System.out.print(sb); // 전체 결과 출력
}
// BFS 함수: 노드 그룹을 탐색하며 방문 처리
public static void bfs(int start, boolean[] visited, ArrayList<Integer>[] al) {
Queue<Integer> q = new LinkedList<>();
q.add(start);
visited[start] = true;
while(!q.isEmpty()) {
int now = q.poll();
for(int next : al[now]) {
if(!visited[next]) {
visited[next] = true;
q.add(next);
}
}
}
}
}