제한 조건을 통해 깊이 우선 탐색 이해하기

이수찬·2023년 5월 27일
0

깊이 우선 탐색은 루트 노드에서 시작해서 다음 분기(branch)로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방법이다.

깊이 우선 탐색은 제한 조건에 따라 일정 부분을 제외하고 전체직인 그래프를 탐색할 때 사용한다.

예시를 들어 dfs에 대해 더욱 자세히 이해해보자.

문제
한 반에 7명의 학생이 존재한다.
해당 반에서 다른 반과 줄다리기 시합을 진행하는데, 서로 싫어하는 학생끼리 이웃하게 세우면, 경기력이 저하된다.
어떻게 하면 정상적인 경기력으로 줄다리기를 할 수 있게 학생들을 일렬로 세울 수 있을까?
정상적인 경기력이 나오도록 반 학생들을 세울 수 있는 모든 방법의 수를 구하시오.

입력 예시

int N = 7; // 반의 총 학생수
int[] arr = new int[][]{{1, 3}, {5, 7}, {4, 2}}; // 서로 싫어하는 학생들의 배열

어떻게 해야 그래프를 탐색할 때 최소한으로 탐색하면서 세울 수 있는 모든 방법의 수를 구할 수 있을까?

가장 간단한 방법으로 생각한다면, 모든 방법의 수를 찾은 후에 세운 방법을 하나씩 싫어하는 학생들의 배열과 비교하는 방법이 존재하지만, 학생수가 많아지거나, 싫어하는 학생들의 배열이 길어지면 시간초과가 발생할 확률이 높다.

다른 방법으로 생각할 수 있는게 깊이 우선 탐색으로 노드들을 탐색하면서, 이전 노드와 현재노드를 비교해, 두 노드가 싫어하는 학생들의 배열에 존재하는지 확인하는 것이다.
이렇게 하면 해당 가지가 싫어하는 학생들의 배열을 포함하는지 계속 확인하기 때문에, 해당 가지의 탐색을 계속 진행해도 될지, 아니면 계속 탐색해야 할지를 알 수 있다.

위의 생각을 코드로 구현해보자.

public int solution(int[][] fight) {

        answer = 0;
        graph = new int[8][8];
        pm = new int[8];

        visited = new int[8];

        for (int[] ints : fight) {
            int x = ints[0];
            int y = ints[1];

            graph[x][y] = 1;
            graph[y][x] = 1;
        }

        dfs(0);
        return answer;
    }
  • 노드간 연결관계를 알고 싶을때는 그래프를 인접리스트 방식으로 구현하는 것보다 인접행렬 방식하는 것이 효율적이기에, 인접행렬 방식으로 구현했다.

  • 그래프는 x에서 y로 이동할 수 없는 경우 1, 이동할 수 있는 경우 0으로 값을 입력했다.
    (서로 싫어하는 학생들을 값을 1로 지정해 그래프를 탐색하면서 1이 나오면 다른 가지로 넘어가서 탐색하도록 만들었다.)

public void dfs(int L) {

        if (L == 7) {
            answer++;

        } else {

            for (int i = 1; i <= 7; i++) {

                if (L >= 1) {
                    if (graph[pm[L]][i] == 1) {
                        continue;
                    }
                }

                if (visited[i] == 0) {
                    visited[i] = 1;
                    pm[L + 1] = i;
                    dfs(L + 1);
                    visited[i] = 0;
                }
            }
        }
    }
  • 깊이 탐색을 하면서 깊이가 7이 되는 경우 학생들을 1열로 세우는 경우가 생긴 것이므로 answer에 1을 더한다.

  • 그래프가 1번 노드부터 시작하기 때문에, for문도 1부터 돈다.

  • L이 1보다 큰 경우, 일렬로 세운 줄에 2명 이상의 학생이 존재하므로, 이전 노드와 현재 노드가 싫어하는 학생들의 배열에 속하는 지 확인하고, 속하면 다음 가지로 이동하기 위해 continue를 실행한다.

  • 속하지 않는다면, 해당 노드를 다시 방문하지 않기 위해 visited 배열로 체크해주고, 현재 방문한 노드를 pm 배열에 저장해둔다.

결론
이렇게 제한 조건에 따라 더 깊은 깊이를 탐색할지 말지를 결정하며, 그래프를 탐색하면 할때 효율적인 탐색이 가능해진다.

0개의 댓글