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

핵심 포인트

  • 비트마스킹 방식으로 활성화 시킬 m개의 바이러스를 선택한다.

  • 선택된 바이러스에 대해서 BFS로 확산시키면서 최소 시간을 기록한다.

import java.util.*;
public class Main {
	static int[][] a;
	static int[] vx = new int[11];
	static int[] vy = new int[11];
	static int[] dx = {1,-1,0,0};
	static int[] dy = {0,0,1,-1};
	static boolean[][] chk;
	static int n;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		int m = sc.nextInt();
		a = new int[n][n];
		chk = new boolean[n][n];
		int vIdx = 0;
		for(int i=0; i<n; i++) {
			for(int j=0; j<n; j++) {
				a[i][j] = sc.nextInt();
				if(a[i][j] == 2) {
          /* vx, vy에 초기 바이러스 위치를 저장한다.
          	 결과적으로 vIdx는 바이러스의 개수를 의미하게 된다.*/
					vx[vIdx] = i;
					vy[vIdx++] = j;
				}
			}
		}
    /* min은 모든 빈칸에 바이러스를 퍼뜨리는 최소 시간을 담는 변수 */
		int min = 3000;
		Queue<Pnt> q = new LinkedList<Pnt>();
    /* 비트마스킹 방식을 사용하여 전체 바이러스의 개수 중에서 활성 상태로 만들 m개를 선택한다. */
		for(int i=0; i<(1<<vIdx); i++) {
			int cnt = 0;
			for(int j=0; j<vIdx; j++)
				if((i & (1<<j)) != 0) cnt++;
			
			if(cnt != m) continue;
      /* 바이러스의 방문 여부를 확인하기 위한 chk 배열 */
			for(int k=0; k<n; k++)
				for(int l=0; l<n; l++)
					chk[k][l] = false;
			/* 활성화 시킬 바이러스의 위치를 queue에 넣어준다. */
			for(int j=0; j<vIdx; j++) {
				if((i & (1<<j)) != 0) {
					q.add(new Pnt(vx[j],vy[j]));
					chk[vx[j]][vy[j]] = true;
				}
			}
			int time = 0;
			while(!q.isEmpty()) {
        /* check 함수는 모든 빈칸에 바이러스가 퍼졌을 때 true를 반환한다. */
				if(check()) {
					if(time < min) min = time;
					break;
				}
				time++;
				int qs = q.size();
				while(qs-- > 0) {
					Pnt p = q.poll();
					for(int k=0; k<4; k++) {
						int ni = p.i + dx[k];
						int nj = p.j + dy[k];
						if(ni < 0 || ni >= n || nj < 0 || nj >= n) continue;
            /* 칸이 벽이거나 이미 방문한 적이 있으면 queue에 넣지 않고 넘어간다. */
						if(a[ni][nj] == 1 || chk[ni][nj]) continue;
						chk[ni][nj] = true;
						q.add(new Pnt(ni,nj));
					}
				}
			}
			q.clear();
		}
    /* min이 갱신되지 않은 경우에는 모든 빈칸에 바이러스를 퍼뜨리는 방법이 없다는 것을 의미한다. */
		if(min == 3000) min = -1;
		System.out.println(min);
	}
  /* 각 칸의 좌표를 담는 Pnt class */
	static class Pnt{
		int i, j;
		public Pnt(int i, int j) {
			this.i = i;
			this.j = j;
		}
	}
  /* 모든 빈칸에 바이러스가 퍼졌을 때 true를 반환하는 check 함수이다. */
	static boolean check() {
		for(int i=0; i<n; i++) {
			for(int j=0; j<n; j++) {
				if(a[i][j] == 1 || a[i][j] == 2 || chk[i][j]) continue;
				return false;
			}
		}
		return true;
	}
}