[BOJ] 17141 연구소2

3 분 소요

BFS 예제

문제

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

문제 풀이

package Baekjoon.Graph.Search;

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

/**
 * Created by kyeahen.
 * Title : 17141 연구소2
 * Category : 완전탐색, bfs
 * Date: 2021/04/09
 */
public class BJ_17141 {

    static class Point {
        int x;
        int y;

        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    static int N, M;
    static int[][] map;
    static boolean[] visited;

    static int[] dx = {1, -1, 0, 0};
    static int[] dy = {0, 0, 1, -1};

    static ArrayList<Point> virus = new ArrayList<>();
    static int ans = Integer.MAX_VALUE;

    static int count = 0; //빈칸 개수

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken()); //연구소 크기
        M = Integer.parseInt(st.nextToken()); //놓을 수 있는 바이러스의 개수

        map = new int[N][N];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {

                if (map[i][j] == 2) { //바이러스를 놓을 수 있는 칸
                    virus.add(new Point(i, j));
                }

                if (map[i][j] == 0) { //빈칸
                    count++;
                }
            }
        }

        /*
        빈칸은 바이러스가 퍼질 수 있는 공간을 의미
        (바이러스를 놓을 수 있는 칸 - 놓을 수 있는 바이러스의 개수)는 바이러스를 놓지 못해서 남은 자리이기에
        빈칸과 같다. 그렇기에 해당 개수만큼 count를 증가시켜준다.
         */

        count += virus.size() - M;
        visited = new boolean[virus.size()]; //바이러스 체크 배열

        if (count == 0) { //빈칸이 없으면 바이러스는 퍼질 수 없음.
            ans = 0;
        }
        else {
            comb(0, 0);
        }

        // "바이러스를 어떻게 놓아도 모든 빈 칸에 바이러스를 퍼뜨릴 수 없는 경우에는 -1을 출력한다."
        if (ans == Integer.MAX_VALUE) {
            System.out.println(-1);
        } else {
            System.out.println(ans);
        }

    }

    //조합 (depth = 바이러스 선택 개수, start = 바이러스 index)
    public static void comb(int depth, int start) {

        if (depth == M) { //바이러스 M개 선택 완료
            int[][] copyMap = copy();
            spreadVirus(copyMap, count);
            return;
        }

        //바이러스 선택
        for (int i = start; i < virus.size(); i++) {
            visited[i] = true;
            comb(depth + 1, i + 1);
            visited[i] = false; //백트래킹
        }
    }

    //바이러스 퍼뜨리기
    public static void spreadVirus(int[][] map, int cnt) {
        Queue<Point> q = new LinkedList<>();

        for (int i = 0; i < virus.size(); i++) {

            if (visited[i]) { //선택한 바이러스를 큐에 삽입
                q.offer(virus.get(i));
            }
        }

        int time = 0; //바이러스가 퍼지는 시간
        while (!q.isEmpty()) {
            if (ans <= time) { break; } // 해당 조합은 이전 조합보다 느리기 때문에 최소 시간이 아님

            int len = q.size();
            for (int v = 0; v < len; v++) { // 시작 지점(바이러스)이 여러 개이기 때문에 반복문으로 한번 더 감싼다.
                Point p = q.poll();

                for (int i = 0; i < 4; i++) {
                    int nx = dx[i] + p.x;
                    int ny = dy[i] + p.y;

                    if (0 <= nx && nx < N && 0 <= ny && ny < N) {

                        //빈칸이 아니면 건너뛰기 (바이러스 - 2, 벽 - 1)
                        if (map[nx][ny] != 0) { continue; }

                        //바이러스 퍼뜨리기
                        map[nx][ny] = 2;
                        q.offer(new Point(nx, ny));
                        cnt--; //빈칸 개수 차감
                    }
                }
            }
            time++; //시간 증가

            if (cnt == 0) { //더 이상 빈칸이 존재하지 않으면 탐색 종료
                ans = time;
                return;
            }

        }
    }

    public static int[][] copy() {
        int[][] temp = new int[N][N]; //map 복사

        //맵 모두 초기화해준 후에 선택된 바이러스만 다시 표시해주기
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {

                if (map[i][j] == 2) { //바이러스를 놓을 수 있는 칸을 0으로 초기화
                    temp[i][j] = 0;
                } else { //빈칸, 벽
                    temp[i][j] = map[i][j];
                }
            }
        }

        for (int i = 0; i < virus.size(); i++) {
            if (visited[i]) { //선택된 바이러스를 2로 표기
                Point v = virus.get(i);
                temp[v.x][v.y] = 2;
            }
        }

        return temp;
    }
}

TMI

21/04/14: 알고리즘 스터디 문제 풀이🤓

댓글남기기