티스토리 뷰
#. Problem
* The copyright in this matter is in BOJ
#. Resolution Process
1. Read and understand problem
2. Redefine the problem + abstract
3. Create solution plan (select Algorithm, Data structure)
4. Prove the plan (check performance time and usage memory)
5. Carry out the plan
6. Look back on the plan and find a way to improve it
#. Solve
코로나 시대..
문제처럼 바이러스가 퍼지는 것이 아니라 백신이 퍼졌으면 하는 바램이다..
이전 연구소 문제를 풀어보았다면 나름 수월하게 풀 수 있는 문제다.
[BOJ] 14502. 연구소(DFS, DFS).java
동작을 간략하게 정리해보면
1. 조합을 활용하여 비활성 바이러스 중 M개를 활성 상태로 변경해주자.
2. 선택되어 활성 상태가 된 M개의 바이러스는 사방으로 퍼진다.
2-1. 활성 바이러스가 비활성 바이러스가 있는 칸으로 가면 비활성 바이러스가 활성으로 변한다.
비활성 바이러스가 활성으로 변하면 바로 사방으로 바이러스가 퍼지게 구현해주어야 한다.
#. Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.StringTokenizer; public class BOJ17142 { static final int max = Integer.MAX_VALUE; static int N, M, cntSpace, minTime, map[][]; static List<Point> virusZone; static Point[] virus; static boolean[][] visited; static Queue<Point> q; static int[] dr= {-1, 0, 1, 0}, dc = {0, 1, 0, -1}; 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()); // 활성 상태로 변경할 수 있는 있는 바이러스의 개수 map = new int[N][N]; virus = new Point[M]; // 선택된 활성 바이러스 virusZone = new ArrayList<>(); // 바이러스를 놓을 수 있는 칸 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()); // 빈 공간일 경우 if(map[i][j] != 1) cntSpace++; // 바이러스를 놓을 수 있는 공간일 경우 if(map[i][j] == 2) virusZone.add(new Point(i, j)); } } minTime = max; cntSpace -= M; // 초기 선택된 바이러스는 제외 process(0, 0); // 어떻게 놓아도 모든 빈 칸에 바이러스를 퍼뜨릴 수 없는 경우 if(minTime == max) System.out.println(-1); else System.out.println(minTime); } private static void process(int cnt, int start) { // 모든 바이러스를 설치 if(cnt == M) { int time = spreadVirus(); minTime = Math.min(minTime, time); return; } for (int i = start; i < virusZone.size(); i++) { // 이 구역 바이러스를 활성 상태로 virus[cnt] = virusZone.get(i); process(cnt + 1, i + 1); } } private static int spreadVirus() { int cntSpread = 0, time = 0, tmp = 0; visited = new boolean[N][N]; q = new LinkedList<>(); // 초기 설치된 바이러스의 위치 for (int i = 0; i < M; i++) { q.add(virus[i]); visited[virus[i].r][virus[i].c] = true; } // 바이러스 확산 while(!q.isEmpty()) { int size = q.size(); boolean space = false; while(size-- > 0) { Point now = q.poll(); for (int d = 0; d < 4; d++) { int rr = now.r + dr[d]; int cc = now.c + dc[d]; // 범위를 벗어나면 pass if(rr < 0 || cc < 0 || rr >= N || cc >= N) continue; // 벽이거나 이미 방문한 곳이면 pass if(map[rr][cc] == 1 || visited[rr][cc]) continue; // 빈 칸일 경우 if(map[rr][cc] == 0) space = true; // 빈 칸이거나 비활성 바이러스일 경우, 감염시키고 Queue에 add visited[rr][cc] = true; cntSpread++; q.add(new Point(rr,cc)); } } // 바이러스의 연쇄 확산일 경우 if(!space) tmp++; else { time += ++tmp; tmp = 0; } } // 모든 빈 칸에 바이러스가 퍼졌다면 if(cntSpread == cntSpace) return time; return max; } static class Point { int r, c; public Point(int r, int c) { this.r = r; this.c = c; } } } | cs |
line 53~69) 조합으로 비활성 바이러스 중 M개를 선택하여 활성 바이러스로 선택
line 56~61) M개의 바이러스를 선택했다면 확산을 시켜보자.
line 63~68) 조합으로 비활성 바이러스를 선택
line 71~118) 바이러스의 확산
line 77~80) 초기 선택된 바이러스를 Queue에 추가해주자.
line 83~113) 바이러스의 확산 과정
line 99) 빈 칸일 경우만 따로 체크를 해주자.
line 101~103) 빈 칸이거나 비활성 바이러스일 경우
방문 체크, 감염된 영역 개수 추가, Queue에 추가
line 108~112) 이 부분이 가장 핵심(?)이다. 하단에 추가 설명
line 116~117) 모든 빈 칸에 바이러스가 퍼졌는지 확인
***
line 108~112) 바이러스가 확산되는데 두 가지 경우가 있다.
1. 연쇄 확산이 일어날 경우
"활성 바이러스가 비활성 바이러스가 있는 칸으로 가면 비활성 바이러스가 활성으로 변한다." 의 동작이
연쇄적으로 일어날 경우.
2. 평범한 확산이 일어날 경우 (빈칸이 바이러스로 확산되었을 경우)
(1번)의 경우에는 연쇄 확산이 일어나므로 모든 확산이 "동시에" 이루어진다.
한 마디로 동시에 바이러스가 사방으로 퍼져버리는 것이다.
이 부분은 모든 칸이 연쇄 확산으로 바이러스가 퍼질 경우를 예외처리해준 부분이라고 생각하면 된다.
연쇄 확산이 일어날 경우도 사실상 전체로 보았을 때 1초가 걸리는 동작이므로 소요 시간을 따로 관리해주자.
마지막 TC를 보면 연쇄 확산의 의미를 이해하기 쉬울 것이다.
Input
5 1 2 2 2 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 2 2 1 1
Output
0
(2번) 평범한 확산이 일어날 경우(빈칸이 바이러스로 확산되었다면)
"활성 바이러스가 비활성 바이러스가 있는 칸으로 가면 비활성 바이러스가 활성으로 변한다." 의 상황이 발생하더라도
빈 칸이 한 칸이라도 확산되었다면,
현재 활성 바이러스가 바로 퍼지나 / 다음 턴에 퍼지나 (어차피 빈 칸(확산된)이 동일한 큐에 존재해서 다음 턴이 와야 함)
동일한 결과를 보인다.
그래서 활성 바이러스로 변한 칸이 있더라도, 빈칸이 하나라도 바이러스로 확산되었다면 그냥 평범한 확산이 일어나게 구현해주면 된다.
line 110) 만일 이전에 연쇄 확산이 일어났었다면 따로 관리해둔 시간을 누적해주자.
* 글로 동작을 설명한다는 것이 정말 어렵다는 것을 다시 한번 깨닫게 되었다..
누군가 읽더라도 이해가 잘 되었으면 좋겠다.. :(
그리고.. 코로나 바이러스가 하루빨리 종식되길..
'PS > Problem_Solving' 카테고리의 다른 글
[BOJ] 4991. 로봇 청소기(BFS, 순열) (0) | 2021.01.18 |
---|---|
[BOJ] 미로 탈출하기(DFS + DP).java (0) | 2020.12.30 |
[BOJ] 3197. 백조의 호수(BFS).java (0) | 2020.12.27 |
[BOJ] 13913. 숨바꼭질 4(BFS, DP).java (0) | 2020.12.26 |
[BOJ] 16234. 인구 이동(BFS, 시뮬레이션).java (0) | 2020.12.26 |