DFS - 백준4963 섬의 개수

이형석·2024년 8월 9일

알고리즘 Phase1

목록 보기
59/59

BFS로 풀 수 있는 문제를 DFS로도 풀 수 있는 경우가 있다.
따라서 다음 문제를 BFS로도 풀어보고, DFS로도 풀어보았다.
백준4963 섬의 개수 : https://www.acmicpc.net/problem/4963

아래 풀이를 보면, DFS가 좀 더 직관적이다.
왜냐하면 칸에 방문하자마자, 방문했다고 표시하고 주변 탐색대상 중 하나를 골라 바로 또 들어간다. 이를 반복하는데, 마지막에 주변에 더 이상 갈 곳이 없으면 return해준다.
이러면 DFS탐색이 끝난다.

BFS의 경우에는 방문한 후, 주변 탐색대상을 먼저 Queue에 넣어둔다. 그리고 그 Queue에 들어가있는 대상들에 대해 차례로 들어간다. 이를 반복하는데, 마지막에 Queue에 더 이상 남은 게 없으면 while문이 끝난다.

위 두 로직을 비교해보면 확실히, 논리적으로 DFS가 더 이해하기 쉽고 BFS는 이게 뭐하는 짓인지 싶은 생각이 든다.

나의 경우에는 지금까지 BFS문제만 풀어보고, DFS로는 처음 접근해봐서 DFS가 아직 익숙하지 않다. 하지만 위와 같이 생각한다면 좀 더 받아들이기 쉬울 것 같다. 그리고 아무래도 재귀함수를 사용하다보니, static 변수를 활용하는 부분에서 백트래킹과 유사하다는 느낌도 든다.

TIP
BFS와 DFS를 선택하는 차이는 다음과 같다. 끝까지 탐색한 후에 다시 돌아와서 무언가 할 일이 있다면 DFS로 재귀를 이용해 푼다.


아래는 각 알고리즘으로 풀어본 코드이다.

BFS

import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //문제
        //섬의 갯수 살리기
        //입력
        //여러개의 테스트 케이스
        //너비 w, 높이 h
        //둘째줄 이하 : 지도 정보
        //땅 : 1, 바다 : 0
        //입력 마지막 줄 : 0 0
        //풀이
        //w와 h가 0 0 으로(마지막입력으로) 들어오면, while(true)를 break
        StringTokenizer st;
        while(true){
            //w, h 입력
            st = new StringTokenizer(br.readLine());
            int y = Integer.parseInt(st.nextToken());
            int x = Integer.parseInt(st.nextToken());
            //마지막입력시 break
            if(x+y == 0){
                break;
            }
            //지도 정보 입력받기
            int[][] jido = new int[x][y];
            for(int i = 0; i < x; i++){
                st = new StringTokenizer(br.readLine());
                for(int j = 0; j < y; j++){
                    jido[i][j] = Integer.parseInt(st.nextToken());
                }
            }
            //알고리즘 실행(섬의 갯수 리턴)
            int ans = bfs(x, y, jido);
            //int ans = dfs(w, h, jido);
            System.out.println(ans);
        }
    }
    static int bfs(int x, int y, int[][] jido){
        int ans = 0;
        //12시부터 시계방향으로 검사
        int[] bx = {0, 1, 1, 1, 0, -1, -1, -1};
        int[] by = {-1, -1, 0, 1, 1, 1, 0, -1};
        //지도탐색 시작
        for(int i = 0; i < x; i++){
            for(int j = 0; j < y; j++){
                //땅이 없으면 continue
                if(jido[i][j] != 1){
                    continue;
                }
                Queue<Point> queue = new LinkedList<>();
                //첫번째칸 방문
                queue.add(new Point(i, j));
                jido[i][j] = 0;
                //bfs순회 시작
                while(!queue.isEmpty()){
                    Point now = queue.remove();
                    for(int k = 0; k < 8; k++){
                        int nx = now.x + bx[k];
                        int ny = now.y + by[k];
                        //ArrayOfBoundary Exception 검사
                        if(nx < 0 || nx >= x || ny < 0 || ny >= y){
                            continue;
                        }
                        //땅이 있는지(또는 방문했는지) 검사
                        if(jido[nx][ny] != 1){
                            continue;
                        }
                        queue.add(new Point(nx, ny));
                        jido[nx][ny] = 0;
                    }
                }
                ans++;
            }
        }
        return ans;
    }
    static class Point{
        int x;
        int y;
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
}

DFS

import java.util.*;
import java.io.*;
public class Main{
    static int x;
    static int y;
    static int[][] jido;
    static int[] bx = {0, 1, 1, 1, 0, -1, -1, -1};
    static int[] by = {-1, -1, 0, 1, 1, 1, 0, -1};
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //문제
        //섬의 갯수 살리기
        //입력
        //여러개의 테스트 케이스
        //너비 w, 높이 h
        //둘째줄 이하 : 지도 정보
        //땅 : 1, 바다 : 0
        //입력 마지막 줄 : 0 0
        //풀이
        //w와 h가 0 0 으로(마지막입력으로) 들어오면, while(true)를 break
        StringTokenizer st;
        while(true){
            //w, h 입력
            st = new StringTokenizer(br.readLine());
            y = Integer.parseInt(st.nextToken());
            x = Integer.parseInt(st.nextToken());
            //마지막입력시 break
            if(x+y == 0){
                break;
            }
            //지도 정보 입력받기
            jido = new int[x][y];
            for(int i = 0; i < x; i++){
                st = new StringTokenizer(br.readLine());
                for(int j = 0; j < y; j++){
                    jido[i][j] = Integer.parseInt(st.nextToken());
                }
            }
            //알고리즘 실행(섬의 갯수 리턴)
            //int ans = bfs(w, h, jido);
            int ans = 0;
            for(int i = 0; i < x; i++){
                for(int j = 0; j < y; j++){
                    //땅이 아니면 continue;
                    if(jido[i][j] != 1){
                        continue;
                    }
                    dfs(i, j);
                    //섬의 갯수++
                    ans++;
                }
            }
            System.out.println(ans);
        }
    }
    static void dfs(int nowX, int nowY){
        //마지막 depth에서 주변에 땅이 없으면 return
        if(jido[nowX][nowY] != 1){
            return;
        }
        //현재 방문한 땅 표시
        jido[nowX][nowY] = 0;
        for(int i = 0; i < 8; i++){
            int nx = nowX + bx[i];
            int ny = nowY + by[i];
            if(nx < 0 || nx >= x || ny < 0 || ny >= y){
                continue;
            }
            if(jido[nx][ny] != 1){
                continue;
            }
            //주변 땅(여덟 방향)중 하나에 대해서 방문
            dfs(nx, ny);
        }
    }
    static class Point{
        int x;
        int y;
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
}
profile
금융IT 개발자

0개의 댓글