코딩테스트/백준

[백준] 14502번

seung_ho_choi.s 2023. 11. 13. 15:01
728x90

사이트

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

 

14502번: 연구소

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

www.acmicpc.net

 

문제

 

 

분석

- 필자가 그렇게 풀고 싶었던 문제를 풀었다... 그래프 문제를 하도 많이 풀어서 그래프 틀 알고리즘을 세우는 것은 어렵지 않았고 오히려 재밌었다! 

- 하지만 벽을 무조건 3개 세우는 알고리즘을 도대체 어떻게 구현을 해야하는지 정말 많은 고민을 해왔다. 전체 경우의 수를 구해서 for 구문을 세울려고 했지만 이거는 너무 노가다일뿐더러 오히려 짜기 복자했고 bfs로 구현할라해도 좀 뭔가 안맞아서 고민을 했다... 결국 다른 분들꺼를 참고해서 분석해본결과 dfs 즉 재귀함수를 이용하면 된것이었다... 하지만 필자가 재귀함수 지식이 너무 부족해 힘들었다... 공부를 많이 해야겠다! 

 

public class Graph14502 {
    static int n;
    static int m;
    static int[][] arr;
    static boolean[][] visited;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};

    static int max = 0;


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        arr = new int[n][m];
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        dfs(0);

        System.out.println(max);
    }

 

입력 부분과 static 변수 선언은 늘 해왔던걸로 하면 된다. 이때 dfs(0)을 호출한다! 메소드명이 좀 그렇지만 dfs()는 벽을 세우기 위해 만든 것이다! 

 

static void dfs(int wall) {
    if (wall == 3) {
        bfs();
        return;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (arr[i][j] == 0) {
                arr[i][j] = 1;
                dfs(wall + 1);
                arr[i][j] = 0;
            }
        }
    }
}

- 필자가 많이 고민했던 문제의 알고리즘이다. 

- 일단 0이 들어오므로 if문에는 안걸린다. 그후 arr 배열에서 0있으면 1로 바꾼뒤 wall+1해서 다시 dfs()를 호출한다. 

- 그러면 wall이 1인 상태로 또 arr 배열에 0이있는지 확인한다. 이떄 처음 wall(0)에서 arr[0][0] 이 0일때 1이 됐으므로 wall(1)에서 arr[0][0]를 건너뛰고 arr[0][1] 이 0일때 1로 바꾼다. 그리고 또 wall(2)를 호출 ..... 그 후 wall(3)일때 if 문에 걸려 bfs()로 가게 된다. 

 

static void bfs() {
    visited = new boolean[n][m];
    Queue<int[]> queue = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (arr[i][j] == 2) {
                queue.add(new int[]{i, j});
            }
        }
    }
    int[][] copyArr = new int[n][m];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            copyArr[i][j] = arr[i][j];
        }
    }

    while (!queue.isEmpty()) {
        int[] temp = queue.poll();
        int staticX = temp[0];
        int staticY = temp[1];
        for (int i = 0; i < 4; i++) {
            int nextX = staticX + dx[i];
            int nextY = staticY + dy[i];
            if (nextX >= 0 && nextX < n && nextY >= 0 && nextY < m) {
                if (copyArr[nextX][nextY] == 0 && !visited[nextX][nextY]) {
                    visited[nextX][nextY] = true;
                    copyArr[nextX][nextY] = 2;
                    queue.add(new int[]{nextX, nextY});
                }
            }
        }
    }

    int cnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (copyArr[i][j] == 0) {
                cnt++;
            }
        }
    }

    max = Math.max(max, cnt);
}

 - 이번 bfs() 함수는 좀 길다..  하나하나 보자 

 

visited = new boolean[n][m];
Queue<int[]> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        if (arr[i][j] == 2) {
            queue.add(new int[]{i, j});
        }
    }
}

 

일단 바이러스 전파를 위해 arr 배열에서 2가 있을때 queue에다가 담아준다. 

 

int[][] copyArr = new int[n][m];
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        copyArr[i][j] = arr[i][j];
    }
}

 

- 그 후 arr 배열을 망가뜨리지 않기 위해 copyarr에 깊은 복사를 한다. 이때 얕은 복사 즉 copyarr=arr이렇게 하게 되면 

주소만 복사 되어 arr이 변경될떄 copy도 변경되서 안된다!!

 

while (!queue.isEmpty()) {
    int[] temp = queue.poll();
    int staticX = temp[0];
    int staticY = temp[1];
    for (int i = 0; i < 4; i++) {
        int nextX = staticX + dx[i];
        int nextY = staticY + dy[i];
        if (nextX >= 0 && nextX < n && nextY >= 0 && nextY < m) {
            if (copyArr[nextX][nextY] == 0 && !visited[nextX][nextY]) {
                visited[nextX][nextY] = true;
                copyArr[nextX][nextY] = 2;
                queue.add(new int[]{nextX, nextY});
            }
        }
    }
}

 

- 이거는 뭐 다들 잘 아시겠지만 바이러스를 전파하는 코드이다. 

 


int cnt = 0;
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        if (copyArr[i][j] == 0) {
            cnt++;
        }
    }
}

max = Math.max(max, cnt);

 

그 후 전파가 끝났으면 copy에서 0 즉 안전구역을 세서 max랑 비교를 한다. 이때 max는 static으로 0으로 선언했기 떄문에

자연스럽게 비교가 된다. 그리고 이 bfs()함수 호출이 끝났으면 

 

static void dfs(int wall) {
    if (wall == 3) {
        bfs();
        return;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (arr[i][j] == 0) {
                arr[i][j] = 1;
                dfs(wall + 1);
                arr[i][j] = 0;
            }
        }
    }
}

 

- 다시 일로와서 return;을 수행하게 된다. 그러면 wall(3)이 끝났으므로 arr[i][j]==0 이 수행하게 된다. 즉 arr[0][2] 가 0이 된다. 다시말해서 0으로 자동으로 바꿔준다. 그 후 for 구문에 따라서 또 0을 1만들고 dfs를 호출하게 되어 계속 반복하게 된다.

- 즉 재귀 함수가 모든 경우의 수를 만들어 놓기 때문에 우리가 일일히 경우의 수를 만들어 안돌려 된다. 

 

전체 코드  

package graph;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Graph14502 {
    static int n;
    static int m;
    static int[][] arr;
    static boolean[][] visited;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};

    static int max = 0;


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        arr = new int[n][m];
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        dfs(0);

        System.out.println(max);
    }

    static void dfs(int wall) {
        if (wall == 3) {
            bfs();
            return;
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (arr[i][j] == 0) {
                    arr[i][j] = 1;
                    dfs(wall + 1);
                    arr[i][j] = 0;
                }
            }
        }
    }


    static void bfs() {
        visited = new boolean[n][m];
        Queue<int[]> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (arr[i][j] == 2) {
                    queue.add(new int[]{i, j});
                }
            }
        }
        int[][] copyArr = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                copyArr[i][j] = arr[i][j];
            }
        }

        while (!queue.isEmpty()) {
            int[] temp = queue.poll();
            int staticX = temp[0];
            int staticY = temp[1];
            for (int i = 0; i < 4; i++) {
                int nextX = staticX + dx[i];
                int nextY = staticY + dy[i];
                if (nextX >= 0 && nextX < n && nextY >= 0 && nextY < m) {
                    if (copyArr[nextX][nextY] == 0 && !visited[nextX][nextY]) {
                        visited[nextX][nextY] = true;
                        copyArr[nextX][nextY] = 2;
                        queue.add(new int[]{nextX, nextY});
                    }
                }
            }
        }

        int cnt = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (copyArr[i][j] == 0) {
                    cnt++;
                }
            }
        }

        max = Math.max(max, cnt);
    }
}

 

 

결과

 

느낀점
아 그 벽세우는 알고리즘때문에 오래걸렸다. 역시 골드 문제다... 재귀함수 즉 다이나믹 프로그래밍 코테문제를 이젠 집중적으로 풀어야겠다. 그래프는 이젠 너무 쉽다. 

728x90