알고리즘/백준

[백준-14052] 연구소

또도닝 2021. 3. 26. 11:06

이 문제는 백준 골드 5 레벨의 문제이다. 

 

처음에는 어떻게 풀어야 할 지 감도 안오고 막막했는데, 단계를 하나씩 나누어서 생각해 보았다.

 

1) 벽은 무조건 3개를 놓아야 하니까, 벽을 설치할 3곳 찾기.

2) 벽 설치할 곳 찾았으면 벽 설치. map에서 벽 부분 1로 바꿔주기 (순열 짜는 코드 이용)

3) 바이러스 퍼트리기 (bfs 이용, 바이러스가 갈 수 있는 모든 곳에 다 보내기)

4) 안전영역 세주기

5) 다음 번을 설치해 주기 위해 원상복구 시키기.

 

이 과정을 완전탐색으로 반복했다. 

원래는 혼자서 bfs 짜는 법도 몰랐는데 조금씩 늘어가고 있는 것 같다.

갈 길이 아직 멀긴 하지만, 천천히 하다보면 조금씩 늘겠지 뭐..

 

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class 연구소_14502 {
	static int N,M;
	static int[][] map;
	static int[][] copy;
	static Point[] wall;
	static boolean[][] check;
	static int ans;
	
	static int[] dr = {-1,1,0,0}; //상하좌우 
	static int[] dc = {0,0,-1,1};
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		M = sc.nextInt();
		map = new int[N][M];
		copy = new int[N][M];
		wall = new Point[3];
		check = new boolean[N][M];
		
		for(int i=0; i<N; i++) { //입력
			for(int j=0; j<M; j++) {
				map[i][j] = sc.nextInt();
				copy[i][j] = map[i][j];
			}
		}
		
		build(0);
		
		System.out.println(ans);
		
	}
	static void build(int cnt){ //벽 세우기 함수
		//자 이제 벽을 놓아보자. 아무데나 다 놓을까.. 
		if(cnt == 3) {
			walls(); //이번에 나온 벽 설치
			spread(); //벽 3개 다 설치 했으니 바이러스 퍼트리기
			count(); //안전영역 세주기
			back(); //원상복구
			return;
		}
		
		for(int i=0; i<N; i++) {
			for(int j=0; j<M; j++) {
				//빈칸이고, 벽 세운적 없고, 세울 수 있다면
				if(map[i][j] == 0 && !check[i][j] && cnt < 3) {
					check[i][j] = true;
					wall[cnt] = new Point(i,j);
					build(cnt+1);
					check[i][j] = false;
				}
			}
		}
		
	}
	
	static void walls() {
		for(int k=0; k<3; k++) {			
			out: for(int i=0; i<N; i++) {
				for(int j=0; j<M; j++) {
					if(wall[k].r == i && wall[k].c == j){
						map[i][j] = 1;
						break out;
					}
				}
			}
		}
	}
	
	static void back() {
		for(int i=0; i<N; i++) {
			for(int j=0; j<M; j++) {
				map[i][j] = copy[i][j];
			}
		}
	}
	
	static void spread(){ //2를 퍼뜨려줄 함수
		Queue<Point> q = new LinkedList<>();
		boolean[][] visited = new boolean[N][M];
		//기존에 존재하는 바이러스 싹 다 넣어줌.
		for(int i=0; i<N; i++) {
			for(int j=0; j<M; j++) {
				if(map[i][j] == 2) {
					q.add(new Point(i,j));
					visited[i][j] = true;
				}
			}
		}
		
		//큐가 끝나면 바이러스가 갈 수 있는 모든 길 다 간 것.
		while(!q.isEmpty()) {
			Point p = q.poll();
			
			for(int i=0; i<4; i++) {
				int nr = p.r + dr[i];
				int nc = p.c + dc[i];
				//칸 넘어가면 pass
				if(nr<0 || nc<0 || nr>=N || nc>=M)
					continue;
				//방문 한 적 있거나 벽이면 pass
				if(visited[nr][nc] && map[nr][nc] == 1)
					continue;
				//빈 칸을 만나면 방문한 적 있다고 체크하고 바이러스 감염시킴.
				if(map[nr][nc] == 0){
					visited[nr][nc] = true;
					map[nr][nc] = 2;
					q.add(new Point(nr,nc));
				}
			}
		}
	}
	
	static void count() { //안전영역 세 줄 함수
		int cnt = 0;
		//map에 0이 몇 개 있는지 세 주기.
		//어차피 벽 놓아보고 바이러스 퍼트리고 셀거라 그냥 세줘도 ㄱㅊ
		for(int i=0; i<N; i++) {
			for(int j=0; j<M; j++) {
				if(map[i][j] == 0)
					cnt++;
			}
		}
		
		if(cnt > ans) {
			ans = cnt;
		}
	}
	
	
	static class Point{
		int r,c;
		Point(int r, int c){
			this.r = r;
			this.c = c;
		}
	}

}