Algorithm
[백준 Gold 5] 15683 감시 - Java
Ray
2021. 12. 26. 23:42
문제링크 : https://www.acmicpc.net/problem/15683
15683번: 감시
스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감
www.acmicpc.net
접근 과정 :
- CCTV의 방향을 다 돌려보면서 완전탐색했다.
- 관건은. CCTV방향을 얼마나 효율적으로 돌리느냐 라고 생각했다.
- 방향을 상하좌우로 나눠서 어떤 하나의 방향이 주어질 때, 그 방향으로 모두 감시하는 함수를 설정하고
- 카메라의 종류에 따라 방향들을 주면서 확인했다.
- 카메라가 보고 있는 곳은 int[][] 에서 -1 시켜가면서 체크했다.
- dfs로 카메라를 하나하나, 카메라의 종류에 따라서 그 방향을 하나하나 체크해가면서 최소 사각지대를 확인했다.
소스 코드 및 결과 :
package BOJ;
/*
감시
*/
import java.io.*;
import java.util.*;
public class BOJ15683 {
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());
office = new int[N][M];
cctvList = new ArrayList<>();
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < M; j++) {
int t = Integer.parseInt(st.nextToken());
if (t > 0 && t < 6) {
cctvList.add(new Cctv(i, j, t));
office[i][j] = -1;
} else if (t == 6) {
office[i][j] = 6;
}
}
}
blind = N * M;
dfs(0);
System.out.println(blind);
}
static int N, M, blind;
static int[][] office;
static List<Cctv> cctvList;
static int[][] dir = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } };
private static class Cctv {
int r;
int c;
int type;
public Cctv(int r, int c, int type) {
this.r = r;
this.c = c;
this.type = type;
}
}
static void dfs(int index) {
if (index == cctvList.size()) {
// 사각지대 체크.
int sum = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (office[i][j] == 0) {
sum++;
}
}
}
blind = Math.min(blind, sum);
return;
}
// cctv 방향 변경
rotate(index);
}
static void watch(int r, int c, int d) {
int nr = r;
int nc = c;
while (true) {
nr += dir[d][0];
nc += dir[d][1];
if (nr < 0 || nr >= N || nc < 0 || nc >= M || office[nr][nc] == 6) {
break;
}
office[nr][nc]--;
}
return;
}
static void unDoWatch(int r, int c, int d) {
int nr = r;
int nc = c;
while (true) {
nr += dir[d][0];
nc += dir[d][1];
if (nr < 0 || nr >= N || nc < 0 || nc >= M || office[nr][nc] == 6) {
break;
}
office[nr][nc]++;
}
return;
}
static void rotate(int index) {
if (index == cctvList.size()) {
return;
}
int r = cctvList.get(index).r;
int c = cctvList.get(index).c;
int t = cctvList.get(index).type;
switch (t) {
case 1:
for (int i = 0; i < 4; i++) {
watch(r, c, i);
dfs(index + 1);
unDoWatch(r, c, i);
}
break;
case 2:
for (int i = 0; i < 2; i++) {
watch(r, c, i);
watch(r, c, (i + 2) % 4);
dfs(index + 1);
unDoWatch(r, c, i);
unDoWatch(r, c, (i + 2) % 4);
}
break;
case 3:
watch(r, c, 0);
watch(r, c, 1);
for (int i = 0; i < 4; i++) {
dfs(index + 1);
unDoWatch(r, c, i);
watch(r, c, (i + 2) % 4);
}
unDoWatch(r, c, 0);
unDoWatch(r, c, 1);
break;
case 4:
watch(r, c, 1);
watch(r, c, 2);
watch(r, c, 3);
for (int i = 0; i < 4; i++) {
dfs(index + 1);
watch(r, c, i);
unDoWatch(r, c, (i + 1) % 4);
}
unDoWatch(r, c, 1);
unDoWatch(r, c, 2);
unDoWatch(r, c, 3);
break;
default: // 5
watch(r, c, 0);
watch(r, c, 1);
watch(r, c, 2);
watch(r, c, 3);
dfs(index + 1);
unDoWatch(r, c, 0);
unDoWatch(r, c, 1);
unDoWatch(r, c, 2);
unDoWatch(r, c, 3);
break;
}
return;
}
}
P.S
어떻게 해야 더 효율적일지 모르겠다.. 그냥 극강의 노가다, 시뮬레이션인 것 같다.
watch / unDoWatch 체크만 방향을 생각해서 최대한 겹치지 않게 돌렸다..
중간에 방향을 헷갈려서 실수가 많이 생겼다.
6(벽)을 사각지대에 포함시키는 실수도 하고...