[백준 / C언어] 2668번: 숫자고르기 (DFS로 풀이)

728x90
728x90

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

 

2668번: 숫자고르기

세로 두 줄, 가로로 N개의 칸으로 이루어진 표가 있다. 첫째 줄의 각 칸에는 정수 1, 2, …, N이 차례대로 들어 있고 둘째 줄의 각 칸에는 1이상 N이하인 정수가 들어 있다. 첫째 줄에서 숫자를 적절

www.acmicpc.net

난이도: solved.ac 골드 5

 

알고리즘 분류


그래프 탐색, DFS (깊이 우선 탐색)

 

접근 방법


방향이 있는 그래프로 생각하면 쉽다. 사이클이 있는 노드들의 개수를 세는 문제다.

문제에 있는 표에서 1행의 숫자들을 노드라고 생각하고, 2행에 있는 숫자들을 1행에 있는 노드에서 연결된 노드라고 보면 된다.

 

구현


1번 노드부터 N번 노드까지 각 노드를 시작점으로 해서 화살표를 따라가 사이클이 생기는지 확인한다.

방문한 노드는 visited에 1로 표시를 해주는데, 화살표를 계속 따라가다가 방문한 노드로 돌아오게 되면 이는 사이클이 생겼다는 걸 의미한다.

 

예를 들어, 1번 노드를 시작점으로 화살표를 계속 따라갔을 때 아래와 모습이 그려진다고 해보자.

6번 → 3번 → 5번 → 6번 노드로 사이클이 생긴다. 여기서 visited에는 1번, 4번, 6번, 3번, 5번 노드가 1로 표시되어 있을 텐데 여기서 사이클이 생긴 부분만 남기고 나머지는 지운다. 따라서 다시 시작점인 1번으로 돌아가 사이클이 시작되는 6번 노드까지의 길을 삭제한다.

 

 

코드


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int graph[101];
int visited[101];

void find(int start);

int main() {
	int N, i, cnt = 0;
	int ans[100];

	scanf("%d", &N);
	for (i = 1; i <= N; i++)
		scanf("%d", &graph[i]);
	
	for (i = 1; i <= N; i++) {
		if (!visited[i]) {
			find(i);
		}
	}

	for (i = 1; i <= N; i++) {
		if (visited[i]) {
			ans[cnt] = i;
			cnt++;
		}
	}

	printf("%d\n", cnt);
	for (i = 0; i < cnt; i++)
		printf("%d\n", ans[i]);
	return 0;
}

void find(int start) {
	int num = start;
	// 사이클 탐색
	while (!visited[num]) {
		visited[num] = 1;
		num = graph[num];
	}
	// 사이클 전까지의 노드 visited에서 제거
	while (start != num) {
		visited[start] = 0;
		start = graph[start];
	}
}
반응형