티스토리 뷰
생각
Github - https://github.com/hellojdh/Code/blob/master/src/baekjoon/swtest/Q14502.java 연구소에서 바이러스가 퍼져나가는 것을 막기위해 벽을 세우려고 한다. 벽은 꼭 3개를 세워야한다. 이 때, 안전 구역의 최대값을 구하여라.
처음 생각
우선 접근하는 방식은 SW Expert의 모의 문제인 벽돌 깨기와 비슷한다. [모의 SW 역량테스트] 벽돌 깨기 이 문제와 마찬가지로 범위의 제한이 아주적다. 벽은 3개이고 맵의 크기도 최대 [8][8]이다. 따라서 완전 탐색을 이용해서 벽 3개를 세울수 있는 모든 조건을 구해서 안전 구역을 구해주었다.
▶ 시간에대해 간단히 계산을 해보면 최대 [8][8]이므로 맵의 크기는 최대 64가 된다. 이중 3개를 선택하는 것이므로 가장 크게 따져도 64x63x62 = 249,984 이며, 새로운 탐색시마다 [8][8] 배열을 초기화 시켜도 2초라는 제한 시간 안에는 아주 거뜬한다.
▶ 실제로는 맵 안에 바이러스나 벽들이 배치되어 있으므로, 최대를 따져도 저거보다는 덜 걸릴 것이다.
다음 생각
다음으로 벽을 세우기 위해서 장소를 택할 때, 3개의 벽을 배치하는 장소가 1차가아니고 2차 배열에서 따져주어야하므로 비트 마스크보다는 재귀를 이용한 DFS 탐색으로 구해주는 것이 편한다.
▶ 이전 문제인 [백준] 15684번 사다리 조작 에서와 같이 2차 배열의 DFS 탐색으로 모든 조합을 구해주었다. 벽을 카운트 하는 변수인 cnt가 3이되면 check()를 해주었다.
다다음 생각
check() 메소드는 해당 조합으로 벽 3개를 설치했을 때, BFS를 이용해서 바이러스를 실제로 퍼트려보는 것이다. 맵이 크지 않으므로 케이스마다 실제로 퍼트려주었다.
▶ 바이러스의 초기 위치는 매번 사용되므로 입력을 받을 때, 미리 List에 넣어놓았다.
▶ 바이러스를 다 퍼트리고 안전 구역을 카운트 해도 상관은 없겠지만 조금이라도 빨리 돌리기위해서 입력 받을 때, 안전 구역을 다 카운트 한 후, 바이러스가 한 번 퍼질 때마다 이 개수를 -1 시켜주었다.
다다다음 생각
위에서 카운트한 안전 구역의 개수는 벽의 위치 3개를 빼놓고 카운트 했으므로, 실제 출력할 때는 -3을 해준 값을 출력하였다.
방안
1) DFS와 BFS를 다 사용하는 벽돌 깨기 문제와 비슷한 문제였다. 물론 BFS를 사용하지 않고 DFS를 사용해도 되지만 퍼트리는 것이 목적이므로 BFS가 더 알맞다.
2) 입력이 적어 Scanner를 통해 입력을 받았다.
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 | package baekjoon.swtest; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Scanner; public class Q14502 { static int n,m; static int[][] arr,copy; static List<Pair> list = new ArrayList<>(); public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); n = sc.nextInt(); // 세로 크기 m = sc.nextInt(); // 가로 크기 arr = new int[n][m]; copy = new int[n][m]; visited = new boolean[n][m]; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { arr[i][j] = sc.nextInt(); // 바이러스 장소 넣어주기 if(arr[i][j]==2) list.add(new Pair(i, j)); else if(arr[i][j]==0) safeCnt++; } } // 결과값 초기화 result = 0; solve(0,0,0); // 벽을 세우는데 3개를 사용했기 때문에 -3을 해준다. System.out.println(result-3); } private static void solve(int x,int y,int cnt) { // 벽 3개를 세웠으면 체크하러 가기 if(cnt==3) { check(); return; } // y index가 범위를 넘겼으면 x를 +1시켜 다음줄로. if(y>=m) { solve(x+1,0,cnt); return; } // x index가 범위를 넘겼으면 끝. if(x>=n) return; // 맵에서 해당 장소가 0 즉, 벽을 세울 수 있다면 세워주자. if(arr[x][y]==0) { arr[x][y] = 1; solve(x,y+1,cnt+1); arr[x][y] = 0; } // 세울 수 없다면 그냥 다음 index로 넘겨주자. solve(x,y+1,cnt); } static int result,safeCnt; static boolean[][] visited; static int[][] dir = {{1,0},{0,1},{-1,0},{0,-1}}; private static void check() { init(); // 검사전 초기화 Queue<Pair> queue = new LinkedList<>(); int safe = safeCnt; // 바이러스 queue에 넣어주기 for(Pair t:list) { visited[t.x][t.y] = true; queue.add(t); } // 바이러스 퍼트리기 while(!queue.isEmpty()) { Pair t = queue.poll(); for(int i=0;i<4;i++) { int tx = t.x+dir[i][0]; int ty = t.y+dir[i][1]; if(tx<0 || ty<0 || tx>=n || ty>=m) continue; if(visited[tx][ty]) continue; if(arr[tx][ty]==0) { visited[tx][ty] = true; queue.add(new Pair(tx, ty)); safe--; // 안전 범위 개수 감소 } } } // safe(이번 케이스의 안전 범위 개수)와 전체 값 비교 result = Math.max(result, safe); } // 검사하기전 맵 & 방문 초기화 private static void init() { for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { copy[i][j] = arr[i][j]; visited[i][j] = false; } } } static class Pair{ private int x,y; Pair(int x,int y){ this.x = x; this.y = y; } } } |
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 15683번 감시 (2) | 2018.10.19 |
---|---|
[백준] 12100번 2048 (Easy) (0) | 2018.10.19 |
[백준] 14890번 경사로 (0) | 2018.10.18 |
[백준] 14499번 주사위 굴리기 (2) | 2018.10.17 |
[백준] 15684번 사다리 조작 (0) | 2018.10.17 |
- Total
- Today
- Yesterday