스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감시할 수 있는 방법은 다음과 같다.
1번 CCTV는 한 쪽 방향만 감시할 수 있다. 2번과 3번은 두 방향을 감시할 수 있는데, 2번은 감시하는 방향이 서로 반대방향이어야 하고, 3번은 직각 방향이어야 한다. 4번은 세 방향, 5번은 네 방향을 감시할 수 있다.
CCTV는 감시할 수 있는 방향에 있는 칸 전체를 감시할 수 있다. 사무실에는 벽이 있는데, CCTV는 벽을 통과할 수 없다. CCTV가 감시할 수 없는 영역은 사각지대라고 한다.
CCTV는 회전시킬 수 있는데, 회전은 항상 90도 방향으로 해야 하며, 감시하려고 하는 방향이 가로 또는 세로 방향이어야 한다.
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 6 0
0 0 0 0 0 0
지도에서 0은 빈 칸, 6은 벽, 1~5는 CCTV의 번호이다. 위의 예시에서 1번의 방향에 따라 감시할 수 있는 영역을 '#'로 나타내면 아래와 같다.
CCTV는 벽을 통과할 수 없기 때문에, 1번이 → 방향을 감시하고 있을 때는 6의 오른쪽에 있는 칸을 감시할 수 없다.
0 0 0 0 0 0
0 2 0 0 0 0
0 0 0 0 6 0
0 6 0 0 2 0
0 0 0 0 0 0
0 0 0 0 0 5
CCTV는 CCTV를 통과할 수 있다. 아래 예시를 보자.
0 0 2 0 3
0 6 0 0 0
0 0 6 6 0
0 0 0 0 0
위와 같은 경우에 2의 방향이 ↕ 3의 방향이 ←와 ↓인 경우 감시받는 영역은 다음과 같다.
# # 2 # 3
0 6 # 0 #
0 0 6 6 #
0 0 0 0 #
사무실의 크기와 상태, 그리고 CCTV의 정보가 주어졌을 때, CCTV의 방향을 적절히 정해서, 사각 지대의 최소 크기를 구하는 프로그램을 작성하시오.
첫째 줄에 사무실의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 8)
둘째 줄부터 N개의 줄에는 사무실 각 칸의 정보가 주어진다. 0은 빈 칸, 6은 벽, 1~5는 CCTV를 나타내고, 문제에서 설명한 CCTV의 종류이다.
CCTV의 최대 개수는 8개를 넘지 않는다.
첫째 줄에 사각 지대의 최소 크기를 출력한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
//백준 15683 감시 -> 브루트포스
public class Main_B15683 {
//입력받은 배열
static int[][]arr;
//1~5번 cctv회전할 수 있는 경우의 수
static int[]cctvCase = {0,4,2,4,4,1};
static int answer=0,n,m;
//cctv수, cctv x,y값, cctv타입 번호
static int cctvCount=0,cctv[][]=new int[8][3];
static void search(int depth){
//모든 cctv감시구역을 체크했을 때
if(depth==cctvCount){
answer=Math.min(answer, getCount());
return;
}
//cctv의 타입에 맞게 회전시킴
for(int d=0;d<cctvCase[cctv[depth][2]];d++){
find(cctv[depth][0],cctv[depth][1],cctv[depth][2],d,0);
search(depth+1);
find(cctv[depth][0],cctv[depth][1],cctv[depth][2],d,1);
}
}
//각 방향으로 cctv감시구역 체크(x값,y값,cctv타입,방향,back 0:감시구역 탐색, 1:탐색했던 구역 되돌리기)
static void check(int x,int y,int type,int d,int back) {
switch (d) {
//하
case 0:
for(int i=x+1;i<n;i++) {
if(back==0&&arr[i][y]<=0)arr[i][y]-=1;
else if(back==1&&arr[i][y]<0)arr[i][y]+=1;
else if(arr[i][y]==6)break;
}
break;
//우
case 1:
for(int i=y+1;i<m;i++) {
if(back==0&&arr[x][i]<=0)arr[x][i]-=1;
else if(back==1&&arr[x][i]<0)arr[x][i]+=1;
else if(arr[x][i]==6)break;
}
break;
//상
case 2:
for(int i=x-1;i>=0;i--) {
if(back==0&&arr[i][y]<=0)arr[i][y]-=1;
else if(back==1&&arr[i][y]<0)arr[i][y]+=1;
else if(arr[i][y]==6)break;
}
break;
//좌
default:
for(int i=y-1;i>=0;i--) {
if(back==0&&arr[x][i]<=0)arr[x][i]-=1;
else if(back==1&&arr[x][i]<0)arr[x][i]+=1;
else if(arr[x][i]==6)break;
}
break;
}
}
//cctv 타입에 맞게 감시구역 탐색
static void find(int x,int y,int type,int d,int back) {
switch (type) {
//1번 cctv
case 1:
switch (d) {
//하
case 0:
check(x,y,type,0,back);
break;
//우
case 1:
check(x,y,type,1,back);
break;
//상
case 2:
check(x,y,type,2,back);
break;
//좌
case 3:
check(x,y,type,3,back);
break;
}
break;
//2번 cctv
case 2:
switch (d) {
//상하
case 0:
check(x,y,type,0,back);
check(x,y,type,2,back);
break;
//좌우
case 1:
check(x,y,type,1,back);
check(x,y,type,3,back);
break;
}
break;
//3번 cctv
case 3:
switch (d) {
//하우
case 0:
check(x,y,type,0,back);
check(x,y,type,1,back);
break;
//상우
case 1:
check(x,y,type,1,back);
check(x,y,type,2,back);
break;
//상좌
case 2:
check(x,y,type,2,back);
check(x,y,type,3,back);
break;
//하좌
case 3:
check(x,y,type,3,back);
check(x,y,type,0,back);
break;
}
break;
//4번 cctv
case 4:
switch (d) {
//하우상
case 0:
check(x,y,type,0,back);
check(x,y,type,1,back);
check(x,y,type,2,back);
break;
//우상좌
case 1:
check(x,y,type,1,back);
check(x,y,type,2,back);
check(x,y,type,3,back);
break;
//상우하
case 2:
check(x,y,type,2,back);
check(x,y,type,3,back);
check(x,y,type,0,back);
break;
//우하좌
case 3:
check(x,y,type,3,back);
check(x,y,type,0,back);
check(x,y,type,1,back);
break;
}
break;
//5번 cctv
case 5:
//상하좌우
for(int i=0;i<4;i++){
check(x,y,type,i,back);
}
break;
}
}
//사각지대 수 return
static int getCount() {
int count=0;
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
if(arr[i][j]==0)count++;
}
}
return count;
}
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());
arr = new int[n][m];
for(int i=0;i<n;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<m;j++) {
int temp = Integer.parseInt(st.nextToken());
arr[i][j]=temp;
//cctv가 설치되지 않았을 때의 값을 answer의 시작값으로
if(temp==0)answer++;
else if(temp>0&&temp<6) {
cctv[cctvCount][0]=i;
cctv[cctvCount][1]=j;
cctv[cctvCount++][2]=temp;
}
}
}
//cctv가 없는 경우 cctv가 설치되지 않았을 때의 값 그대로 print
if(cctvCount==0)System.out.println(answer);
else {
search(0);
System.out.println(answer);
}
}
}