-
[17142] 연구소 3 - Java[자바]자바/백준 2023. 10. 15. 23:44
| 문제 링크
https://www.acmicpc.net/problem/17142
17142번: 연구소 3
인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 바이러스는 활성 상태와 비활성 상태가 있다. 가장 처음에 모든 바이러스는 비활성 상태이고,
www.acmicpc.net
| 문제
- NxN 크기의 연구소에 바이러스가 있습니다. N(4 ≤ N ≤ 50)
0: 빈 칸
1: 벽
2: 바이러스 - 바이러스는 0인 빈 칸으로 퍼져나갈 수 있으며, 1인 벽으로는 전파되지 못합니다.
- 연구소에 놓인 바이러스 중 M개만 활성화 시킬 수 있으며, 나머지는 비활성화 상태로 활성화된 바이러스가 닿으면 활성화 상태로 바뀝니다. M(1 ≤ M ≤ 10)
- M개의 바이러스를 활성화 시켰을 때, 연구소의 모든 빈 칸에 바이러스를 퍼트릴 수 있는 최소 시간(바이러스가 한 칸 이동하는데 걸리는 시간은 1로 계산) 을 출력하시오.
만약, 모든 빈 칸에 바이러스를 퍼트릴 수 없다면, -1을 출력하시오.
| 풀이
- 연구수 배열의 값을 입력 받으면서, '빈 칸의 값을 카운트', '바이러스들의 위치를 배열에 저장' 두 가지 행동을 같이 진행해줍니다.
(빈칸이 0인 경우 0을 출력) - DFS를 통해 어떤 바이러스를 활성화 시킬 것인지 부분 수열을 구합니다.
- 2번에서 구한 활성화된 바이러스 부터 BFS 탐색을 진행합니다.
- 사방 탐색으로 바이러스를 퍼트리며, 퍼트린 공간을 방문처리 하여 재방문하지 않도록하며, 빈 칸의 카운트를 -1씩 감소시킵니다.
- 카운트가 0이 되었을 때, 모두 전파하기 까지 걸린 시간의 최소값을 비교합니다.
| 풀이
import java.io.*; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.StringTokenizer; public class Main { public static int N; public static int M; public static int placeCnt=0; public static int min = Integer.MAX_VALUE; public static int[][] lab; public static List<int[]> virus = new ArrayList<>(); public static int[][] virusFlag; public static boolean[][] visit; public static int[][] delta = {{-1,0},{0,1},{1,0},{0,-1}}; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); lab = new int[N][N]; //연구소 값 입력 for(int r = 0; r<N; r++) { st = new StringTokenizer(br.readLine()); for(int c = 0; c<N; c++) { int num = Integer.parseInt(st.nextToken()); lab[r][c] = num; //바이러스는 별도 위치를 저장 if(num == 2) { virus.add(new int[] {r,c,0}); } //바이러스가 퍼질 수 있는 빈 칸의 개수를 카운트 else if(num == 0) placeCnt++; } } //어떤 바이러스를 활성화 시킬지 여부를 표기하기 위한 배열 virusFlag = new int[M][1]; //만약 빈 칸이 없는 경우 0을 출력 if(placeCnt==0) min = 0; //빈 칸이 있는 경우 dfs를 진행 else dfs(0,0); //바이러스가 다 퍼질 수 없는 경우 -1을 출력 if(min == Integer.MAX_VALUE) min = -1; bw.write(String.valueOf(min)); br.close(); bw.close(); } public static void dfs(int depth, int idx) { //M개의 바이러스를 활성화 시킨 경우 bfs를 통해 바이러스를 확산 시킴 if(depth==M) { visit = new boolean[N][N]; bfs(); return; } //몇 번째 바이러스를 활성화 시킬 것인지 부분 수열을 구함 for(int i = idx; i<virus.size(); i++) { virusFlag[depth] = virus.get(i); dfs(depth+1, i+1); } } public static void bfs() { //빈 칸을 모두 바이러스로 채운 경우(cnt가 0이 되는 경우)를 확인하기 위해 빈 칸의 개수를 복사 int cnt = placeCnt; Queue<int[]> que = new LinkedList<>(); //활성화 시키 바이러스의 좌표값을 que에 입력 for(int i = 0; i<M; i++) { que.add(virusFlag[i]); visit[virusFlag[i][0]][virusFlag[i][1]] = true; } while(!que.isEmpty()) { int[] rc = que.poll(); int r = rc[0]; int c = rc[1]; int w = rc[2]; //바이러스로부터 4방 탐색 진행 for(int i = 0; i<4; i++) { int dr = r+delta[i][0]; int dc = c+delta[i][1]; //인덱스 범위를 벗어나거나, 이미 바이러스가 퍼졌거나, 벽인 경우는 continue if(dr<0 || dr>=N || dc<0 || dc>=N || visit[dr][dc] || lab[dr][dc]==1) continue; //바이러스를 퍼트렸으면 방문처리 visit[dr][dc] = true; //바이러스를 퍼트린 공간이 빈 칸이라면 cnt--(바이러스를 퍼트린 공간이 비활성화된 바이러스 위치일수도 있기에) if(lab[dr][dc]==0) cnt--; //모든 칸에 바이러스를 퍼트렸다면, 바이러스가 퍼지기까지 걸린 시간의 min값을 구함 if(cnt==0) { min = Math.min(min, w+1); return; } //아직 빈 칸이 남아있는 경우 바이러스를 게속 퍼트핌 que.add(new int[] {dr,dc,w+1}); } } } }
'자바 > 백준' 카테고리의 다른 글
[11054] 가장 긴 바이토닉 부분 수열 - Java[자바] (0) 2023.10.16 [14002] 가잔 긴 증가하는 부분 수열 4 - Java[자바] (0) 2023.10.16 [13460] 구슬 탈출 2 - Java[자바] (1) 2023.10.11 [14621] 나만 안되는 연애 - Java[자바] (1) 2023.10.10 [4386] 별자리 만들기 - Java[자바] (1) 2023.10.09 - NxN 크기의 연구소에 바이러스가 있습니다. N(4 ≤ N ≤ 50)