문제
접근 방식
문제를 보면 Cctv의 방향을 바꿔가며 최소의 사각지대 수를 찾아 출력해야 하기 때문에 방향을 중복순열로 구하고 해당 방향에 맞게 Cctv의 감시영역을 #으로 채우고 사각지대 값을 구해가며 최솟값을 찾는 방식이 떠올랐다
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main_15683_정현명 {
	static int N,M;
	static int mat[][];
	static List<Cctv> cctvs;
	static int deltaIdx[];
	static int[][] deltas = {{-1,0},{0,1},{1,0},{0,-1}};
	static int minBlindSpot;
	
	// Cctv 클래스 ( 행과 열을 가짐 )
	public static class Cctv{
		int r;
		int c;
		
		Cctv(int r, int c){
			super();
			this.r = r;
			this.c = c;
		}
	}
		
	public static void main(String[] args) throws IOException{
		// ================== 입력 ===================
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		N = Integer.parseInt(st.nextToken()); // 행
		M = Integer.parseInt(st.nextToken()); // 열
		
		mat = new int[N][M]; // 사무실 각 칸 정보
		cctvs = new ArrayList<>(); // Cctv 객체 배열
		
		for(int i=0;i<N;i++) {
			st = new StringTokenizer(br.readLine());
			for(int j=0;j<M;j++) {
				mat[i][j] = Integer.parseInt(st.nextToken());
				if(mat[i][j] <=5 && mat[i][j] >=1) cctvs.add(new Cctv(i,j));
			}
		}
		
		// ================== 솔루션 ==================
		
		deltaIdx = new int[cctvs.size()]; // cctv 순으로 방향을 가지는 배열
		minBlindSpot = N * M; // 사각지대 최솟값
		perRep(0); // 중복순열함수 실행
		
		
		// ================== 출력 ===================
		System.out.println(minBlindSpot);
	}
	
	
	// cctv 방향으로 중복순열을 만드는 함수
	public static void perRep(int cnt) {
		// 모든 cctv의 방향이 결정되면 사각지대를 찾는 함수 호출
		if(cnt == cctvs.size()) {
			searchBlindSpot();
			return;
		}
		
		for(int i=0;i<4;i++) {
			deltaIdx[cnt] = i;
			perRep(cnt+1);
		}
	}
	
	// 사각지대를 찾는 함수
	public static void searchBlindSpot() {
		
		// 사무실 각 칸 깊은 복사
		int tempMat[][] = new int[N][M];
		
		for(int i=0;i<N;i++) {
			for(int j=0;j<M;j++) {
				tempMat[i][j] = mat[i][j];
			}
		}
		
		
		// 각 cctv에 대해 만들어 놓은 중복 순열 방향으로 Cctv 번호에 맞게 칸을 채움
		for(int i=0;i<cctvs.size();i++) {
			Cctv cctv = cctvs.get(i);
			int way = deltaIdx[i];
			
			switch(tempMat[cctv.r][cctv.c]) {
			
			case 1:
				tempMat = fillPound(way, tempMat, cctv.r, cctv.c);
				break;
			
			case 2:
				tempMat = fillPound(way, tempMat, cctv.r, cctv.c);
				tempMat = fillPound((way+2)%4, tempMat, cctv.r, cctv.c);
				break;
			
			case 3:
				tempMat = fillPound(way, tempMat, cctv.r, cctv.c);
				tempMat = fillPound((way+1)%4, tempMat, cctv.r, cctv.c);
				break;
				
				
			case 4 :
				tempMat = fillPound(way, tempMat, cctv.r, cctv.c);
				tempMat = fillPound((way+1)%4, tempMat, cctv.r, cctv.c);
				tempMat = fillPound((way+2)%4, tempMat, cctv.r, cctv.c);
				break;
				
			case 5 :
				tempMat = fillPound(0, tempMat, cctv.r, cctv.c);
				tempMat = fillPound(1, tempMat, cctv.r, cctv.c);
				tempMat = fillPound(2, tempMat, cctv.r, cctv.c);
				tempMat = fillPound(3, tempMat, cctv.r, cctv.c);
				break;
			}
		}
		
		countBlindSpot(tempMat); // 사각지대 횟수 카운팅
		
	}
	
	// 우물정자 #을 받아온 방향에 맞게 벽을 만나거나 사무실 밖으로 나가기 전까지 채움
	public static int[][] fillPound(int way, int[][] tempMat, int r, int c) {
		while(true) {
			r += deltas[way][0];
			c += deltas[way][1];
			if(r < 0 || r >= N || c < 0 || c >= M || tempMat[r][c] == 6)break;
			if(tempMat[r][c] != 0) continue; // 다른 Cctv를 만날 경우 넘어가기
			tempMat[r][c] = '#'; // 0을 #으로 채우기
		}
		
		return tempMat;
	}
	
	// 사각지대 횟수 카운팅 함수
	public static void countBlindSpot(int[][] tempMat) {
		int cnt = 0;
		for(int i=0;i<N;i++) {
			for(int j=0;j<M;j++) {
				if(tempMat[i][j] == 0) cnt++;
			}
		}
		
		minBlindSpot = minBlindSpot < cnt ? minBlindSpot : cnt;
	}
}