문제 풀이 날짜: 2023.09.01
포스트 작성일: 2023.09.11

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

백준 온라인 저지 15683번: 감시 (골드4)

 

키워드

완전 탐색, 백트래킹

 

풀이 접근법

  • 사각지대의 최소 크기 ⇒ matrix에 남아있는 0의 개수가 최소가 되도록 한다.
  • 각 CCTV마다 설치 가능한 방향을 완전탐색으로 찾는다.
    • 1. CCTV의 좌표를 리스트로 취합한다.
    • 2. 가능한 방향마다 회전시켜서 감시 범위를 설정한다.
      • 중복되는 감시 범위를 체크/체크해제 해야 하므로 scope 배열은 int[ ][ ]로 선언한다.
    • 3. 다음 재귀를 탐색한다.
    • 4. 재귀에서 리턴받았다면 감시 범위를 해제시킨다.
    • 5. 기저사례에 도달했다면 배열에 남은 0의 개수(카메라가 닿지 않는 곳)를 센다.
  • 카메라의 회전은 어떻게 구현할까?
    • CCTV 방향별로 switch문을 통해 체크한다 ⇒ 각 방향마다 모두 배열에 표시해본 후 유망한 것을 찾는다.

 

코드

import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

	private static final int BLANK = 0;
	private static final int WALL = 6;
	
	private static int N;
	private static int M;
	
	private static int count;
	private static int min = Integer.MAX_VALUE;
	
	private static int[][] matrix;
	private static int[][] scopes;
	
	private static List<Point> cameras = new ArrayList<>();
	
	private static int[] dr = {1, 0, -1, 0};
	private static int[] dc = {0, 1, 0, -1};
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		String[] temp = br.readLine().split(" ");
		
		N = Integer.parseInt(temp[0]);
		M = Integer.parseInt(temp[1]);

		matrix = new int[N][M];
		scopes = new int[N][M];
		
		StringTokenizer st;
		for(int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());
			for(int j = 0; j < M; j++) {
				matrix[i][j] = Integer.parseInt(st.nextToken());
				
				//5번 카메라는 제외하고 add
				if(matrix[i][j] != BLANK && matrix[i][j] != WALL 
						&& matrix[i][j] != 5) {
					cameras.add(new Point(j, i));
				}
				
				if(matrix[i][j] != BLANK) {
					scopes[i][j] = -1; //사각지대에서 제외시킬 곳 기록
				}
			}
		}
		
		for(int i = 0; i < N; i++) {
			for(int j = 0; j < M; j++) {
				if(matrix[i][j] == 5) { //회전시킬 필요 없는 카메라는 미리 세팅한다.
					for(int dir = 0; dir < dr.length; dir++) {
						setScope(scopes, new Point(j, i), dir, 1);
					}
				}
			}
		}
		
		count = cameras.size(); //5번 카메라를 제외한 카메라의 수

		DFS(scopes, 0);
		
		System.out.println(min);
	}
	
	private static void DFS(int[][] scopes, int depth) {
		if(depth == count) {
			int result = 0;
			for(int i = 0; i < N; i++) {
				for(int j = 0; j < M; j++) {
					if(scopes[i][j] == BLANK) result += 1;
				}
			}

			min = Math.min(result, min);
			
			return;
		}
		
		Point curr = cameras.get(depth);
		int r = curr.y;
		int c = curr.x;
		
		switch(matrix[r][c]) { //카메라 방향 별 시야 기록
			case 1: {
				for(int dir = 0; dir < dr.length; dir++) {
					setScope(scopes, curr, dir, 1);
					DFS(scopes, depth + 1);
					setScope(scopes, curr, dir, -1); //원복
				}
				break;
			}
			case 2: {
				for(int dir = 0; dir < dr.length / 2; dir++) {
					setScope(scopes, curr, dir, 1);
					setScope(scopes, curr, dir + 2, 1); //dir과 대칭 방향
					DFS(scopes, depth + 1);
					setScope(scopes, curr, dir, -1); //원복
					setScope(scopes, curr, dir + 2, -1);
				}
				break;
			}
			case 3: {
				for(int i = 0; i < dr.length; i++) {
					for(int j = i + 1; j < dr.length; j++) { //서로 다른 두 방향 i, j를 탐색 (대칭X)
						if(i == j || Math.abs(i - j) == 2) { continue; }
						setScope(scopes, curr, i, 1);
						setScope(scopes, curr, j, 1); //dir과 대칭 방향
						DFS(scopes, depth + 1);
						setScope(scopes, curr, i, -1); //원복
						setScope(scopes, curr, j, -1);
					}
				}
				break;
			}
			case 4: {
				for(int k = 0; k < dr.length; k++) {
					for(int dir = 0; dir < dr.length; dir++) {
						if(k == dir) { continue; } //k번째 방향을 제외한 세 방향 탐색
						setScope(scopes, curr, dir, 1);
					}
					
					DFS(scopes, depth + 1);
					
					for(int dir = 0; dir < dr.length; dir++) {
						if(k == dir) { continue; } //k번째 방향을 제외한 세 방향 탐색
						setScope(scopes, curr, dir, -1); //원복
					}
				}
				break;
			}
		}
		
	}
	
	private static void setScope(int[][] scope, Point start, int dir, int target) {
		int nr = start.y;
		int nc = start.x;
		while(true) {
			nr += dr[dir];
			nc += dc[dir];
			
			//벽이라면 탐색을 멈춘다.
			if(!isValid(nr, nc) || matrix[nr][nc] == WALL) { return; }
			
			//다른 숫자라면 계속 탐색한다. 0인 경우에만 target으로 덮어씌운다.
			if(matrix[nr][nc] == BLANK) { scope[nr][nc] += target; }
		}
	}

	private static boolean isValid(int nr, int nc) {
		return (nr > -1 && nc > -1 && nr < N && nc < M);
	}

}

 

 

git 링크

(git 링크 첨부)

+ Recent posts