본문 바로가기

알고리즘/BFS, DFS

[백준] 14502번 연구소 java[브루트포스, 탐색][엄탱]

728x90

문제 링크

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

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크

www.acmicpc.net

문제 풀이

백준 14502번 연구소 문제는 두 가지만 어떤 방식으로 풀지 알면 그 뒤로는 금방 풀 수 있다.

3개의 벽을 어떻게 설치할지바이러스 퍼지는 영역을 어떻게 찾을지만 생각해보고 코드를 보면 이해하기 쉽다.

  • 브루트포스 알고리즘(직접구현 또는 DFS): 3개의 벽을 설치할 때 사용
  • 너비 우선 탐색(BFS): 바이러스가 퍼지는 영역을 찾는 데 사용

시간복잡도를 생각해보자

지도의 최대 크기 = 8 * 8 = 64
바이러스 퍼지는 최대 영역 = 지도 최대 크기
벽 1개를 설치하는 경우의 수 = 64
벽 2개를 설치하는 경우의 수 = 64 * 64 = 4,096
벽 3개를 설치하는 경우의 수 = 64 * 64 * 64 = 262,144
벽 3개를 설치하고 바이러스 퍼지는 영역을 검사하는 최대 경우의 수 = 262,144 * 64 = 16,777,216

시간제한은 2초이고 경우의 수가 최대 1억을 넘지 않으니 가능한 것으로 보인다.

코드(DFS)

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int N; // 행의 개수 (세로 길이)
    static int M; // 열의 개수 (가로 길이)
    static int[][] board; // 연구소 지도
    static int zeroCount = 0; // 처음 연구소에서 0개의 개수
    static int max = 0;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        board = new int[N][M];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine(), " ");

            for (int j = 0; j < M; j++) {
                board[i][j] = Integer.parseInt(st.nextToken());

                if (board[i][j] == 0) {
                    zeroCount++;
                }
            }
        }
        
        wallDFS(0, 0);
        
        System.out.println(max);
        br.close();
    }
    public static void wallDFS(int count, int index) {
        if (count == 3) {
            max = Math.max(max, zeroCount - birusCountBFS() - 3);
            return;
        }
        
        for (int i = index; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (board[i][j] == 0) {
                    board[i][j] = 1;
                    wallDFS(count + 1, i);
                    board[i][j] = 0;
                }
            }
        }
    }

    public static int birusCountBFS() {
        int count = 0;

        Queue<int[]> q = new LinkedList<>();

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (board[i][j] == 2) {
                    q.offer(new int[]{i, j});
                }
            }
        }

        boolean[][] visited = new boolean[N][M];

        while(!q.isEmpty()) {
            int[] cur = q.poll();

            int x = cur[0];
            int y = cur[1];

            if (visited[x][y]) {
                continue;
            }

            if (board[x][y] == 0) {
                count++;
            }

            visited[x][y] = true;

            if (x - 1 >= 0 && !visited[x - 1][y] && board[x - 1][y] == 0) {
                q.offer(new int[]{x - 1, y});
            }

            if (x + 1 < N && !visited[x + 1][y] && board[x + 1][y] == 0) {
                q.offer(new int[]{x + 1, y});
            }

            if (y - 1 >= 0 && !visited[x][y - 1] && board[x][y - 1] == 0) {
                q.offer(new int[]{x, y - 1});
            }

            if (y + 1 < M && !visited[x][y + 1] && board[x][y + 1] == 0) {
                q.offer(new int[]{x, y + 1});
            }
        }

        return count;
    }
}

코드(DFS를 사용하지 않고 구현)

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int N; // 행의 개수 (세로 길이)
    static int M; // 열의 개수 (가로 길이)
    static int[][] board; // 연구소 지도
    static int zeroCount = 0; // 처음 연구소에서 0개의 개수
    static int max = 0;
    
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        board = new int[N][M];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine(), " ");

            for (int j = 0; j < M; j++) {
                board[i][j] = Integer.parseInt(st.nextToken());

                if (board[i][j] == 0) {
                    zeroCount++;
                }
            }
        }
        
        for (int i = 0; i < N; i++) {
            for (int _i = 0; _i < M; _i++) {
                if (board[i][_i] == 0) {
                    board[i][_i] = 1;

                    for (int j = i; j < N; j++) {
                        for (int _j = 0; _j < M; _j++) {
                            if (board[j][_j] == 0) {
                                board[j][_j] = 1;

                                for (int k = j; k < N; k++) {
                                    for (int _k = 0; _k < M; _k++) {
                                        if (board[k][_k] == 0) {
                                            board[k][_k] = 1;
                                            int count = birusCountBfs();
                                            max = Math.max(max, zeroCount - count - 3);
                                            board[k][_k] = 0;
                                        }
                                    }
                                }

                                board[j][_j] = 0;
                            }
                        }
                    }

                    board[i][_i] = 0;
                }
            }
        }
        
        System.out.println(max);
        br.close();
    }

    public static int birusCountBfs() {
        int count = 0;

        Queue<int[]> q = new LinkedList<>();

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (board[i][j] == 2) {
                    q.offer(new int[]{i, j});
                }
            }
        }

        boolean[][] visited = new boolean[N][M];

        while(!q.isEmpty()) {
            int[] cur = q.poll();

            int x = cur[0];
            int y = cur[1];

            if (visited[x][y]) {
                continue;
            }

            if (board[x][y] == 0) {
                count++;
            }

            visited[x][y] = true;

            if (x - 1 >= 0 && !visited[x - 1][y] && board[x - 1][y] == 0) {
                q.offer(new int[]{x - 1, y});
            }

            if (x + 1 < N && !visited[x + 1][y] && board[x + 1][y] == 0) {
                q.offer(new int[]{x + 1, y});
            }

            if (y - 1 >= 0 && !visited[x][y - 1] && board[x][y - 1] == 0) {
                q.offer(new int[]{x, y - 1});
            }

            if (y + 1 < M && !visited[x][y + 1] && board[x][y + 1] == 0) {
                q.offer(new int[]{x, y + 1});
            }
        }

        return count;
    }
}

기타

근데 항상 브루트포스 알고리즘으로 생각하는 게 더 어렵다.

'이렇게 하나하나 해도 될까? 아니야 안될 것 같아' 라는 생각 때문에 분명 시간초과가 날 것 같고 안될것 같다.

그래서 항상 브루트포스 문제로 접근해서 습관을 만들고 그다음 시간초과가 나면 어떻게 하면 효율적으로 풀 수 있을지 생각해보는 게 좋은 것 같다. 

물론, 시간복잡도를 계산하고 브루트포스 문제를 푸는 게 좋은 것 같다.

 

728x90