15683 감시 [JAVA]
문제 분석
https://www.acmicpc.net/problem/15683
n*m 배열이 주어진다. 각 배열의 원소는 값에 따라 아래의 역할을 한다.
0 : 빈 땅
1~5 : CCTV
6 : 벽
각 CCTV는 1~5의 값에 따라 감시할 수 있는 방향의 유형이 정해져있고, 특정 방향으로 바라보는 CCTV는 배열의 끝 혹은 벽을 만나기전까지 감시를 할 수 있다.
CCTV의 방향은 아래와 같다.
CCTV는 90도씩 회전할 수 있다.
이때, CCTV들을 특정 각도로 회전시키며 배열을 감시할 때 사각지대의 최솟값을 구하는게 문제다.
여기서 주의할 점은, 벽과 CCTV가 존재하는 곳은 사각지대로 세지 않는다.
문제를 객체지향적으로 접근한다면, 즉 조금 더 세분화해서 쪼개보면 각 CCTV를 조금 더 단순하게 표현할 수 있다.
1 -> 동,서,남,북 중 하나의 방향을 비춘다
2 -> 동-서, 남-북 중 하나의 방향을 비춘다
3 -> 북-동, 동-남, 남-서, 서-북 중 하나의 방향을 비춘다.
4 -> 서-북-동, 북-동-남, 동-남-서, 남-서-북 중 하나의 방향을 비춘다.
5 -> 동-서-남-북을 비춘다.
동,서,남,북에 대해 비추는 방향에 따라 볼 수 있는 (사각지대를 없앨 수 있는) 점은 독립이기에 해당 기능을 별도의 메소드로 각각 만들어 이를 엮어서 사용할 수 있다.
따라서, 나는 BFS로 각 CCTV를 순서대로 탐색하고
각 CCTV의 타입에 따라 비출 수 있는 방향의 경우의 수에 대해 동,서,남,북을 비추는 메소드를 이용하여 새롭게 비춰진 점들을 구하고
다음 depth로 넘어가는 것을 반복한다.
그렇게 모든 CCTV에 대해 반복을 마쳤다면 그 상태의 배열의 사각지대의 수를 세고 이를 최솟값과 비교하여 저장한다.
다시 돌아와서는 설정한 방향의 점들을 revert 하고 다음 방향으로 cctv를 돌려 다시 다음 depth를 탐색한다.
위 과정을 수행하는 BFS를 수행하게 된다면 결국에는 모든 CCTV가 비출 수 있는 모든 방향에 대해 사각지대의 수를 계산할 수 있게 된다.
코드
import java.text.ParsePosition;
import java.util.*;
public class Main {
static int[][] board;
static List<CCTV> cctvList;
static int minHiddenCount;
static int n,m;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
board = new int[n][m];
cctvList = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
board[i][j] = sc.nextInt();
if(board[i][j] >= 1 && board[i][j] <=5){
cctvList.add(new CCTV(new Point(i,j),board[i][j]));
}
}
}
minHiddenCount = n*m;
findAllRoot(0);
System.out.println(minHiddenCount);
}
static void findAllRoot(int nowIdx){
if(nowIdx >= cctvList.size()){
minHiddenCount = Math.min(minHiddenCount, countHiddenPoints());
return;
}
CCTV cctv = cctvList.get(nowIdx);
Set<Point> upPoints = seeUpward(cctv.point);
Set<Point> downPoints = seeDownward(cctv.point);
Set<Point> rightPoints = seeRightward(cctv.point);
Set<Point> leftPoints = seeLeftward(cctv.point);
switch (cctv.type){
case 1:
markPoints(upPoints);
findAllRoot(nowIdx+1);
revertPoints(upPoints);
markPoints(downPoints);
findAllRoot(nowIdx+1);
revertPoints(downPoints);
markPoints(rightPoints);
findAllRoot(nowIdx+1);
revertPoints(rightPoints);
markPoints(leftPoints);
findAllRoot(nowIdx+1);
revertPoints(leftPoints);
break;
case 2:
markPoints(upPoints);
markPoints(downPoints);
findAllRoot(nowIdx+1);
revertPoints(upPoints);
revertPoints(downPoints);
markPoints(rightPoints);
markPoints(leftPoints);
findAllRoot(nowIdx+1);
revertPoints(rightPoints);
revertPoints(leftPoints);
break;
case 3:
markPoints(upPoints);
markPoints(rightPoints);
findAllRoot(nowIdx+1);
revertPoints(upPoints);
markPoints(downPoints);
findAllRoot(nowIdx+1);
revertPoints(rightPoints);
markPoints(leftPoints);
findAllRoot(nowIdx+1);
revertPoints(downPoints);
markPoints(upPoints);
findAllRoot(nowIdx+1);
revertPoints(leftPoints);
revertPoints(upPoints);
break;
case 4:
markPoints(leftPoints);
markPoints(upPoints);
markPoints(rightPoints);
findAllRoot(nowIdx+1);
revertPoints(leftPoints);
markPoints(downPoints);
findAllRoot(nowIdx+1);
revertPoints(upPoints);
markPoints(leftPoints);
findAllRoot(nowIdx+1);
revertPoints(rightPoints);
markPoints(upPoints);
findAllRoot(nowIdx+1);
revertPoints(upPoints);
revertPoints(downPoints);
revertPoints(leftPoints);
break;
case 5:
markPoints(leftPoints);
markPoints(upPoints);
markPoints(rightPoints);
markPoints(downPoints);
findAllRoot(nowIdx+1);
revertPoints(upPoints);
revertPoints(downPoints);
revertPoints(leftPoints);
revertPoints(rightPoints);
break;
default:
throw new RuntimeException("Impossible type");
}
}
static Set<Point> seeUpward(Point point){
Set<Point> addedPoints = new HashSet<>();
for(int i = point.x-1; i >= 0; i--){
if(board[i][point.y] == 6)
break;
if(board[i][point.y] == 0)
addedPoints.add(new Point(i, point.y));
}
return addedPoints;
}
static Set<Point> seeDownward(Point point){
Set<Point> addedPoints = new HashSet<>();
for(int i = point.x+1; i < n; i++){
if(board[i][point.y] == 6)
break;
if(board[i][point.y] == 0)
addedPoints.add(new Point(i, point.y));
}
return addedPoints;
}
static Set<Point> seeRightward(Point point){
Set<Point> addedPoints = new HashSet<>();
for(int i = point.y+1; i < m; i++){
if(board[point.x][i] == 6)
break;
if(board[point.x][i] == 0)
addedPoints.add(new Point(point.x, i));
}
return addedPoints;
}
static Set<Point> seeLeftward(Point point){
Set<Point> addedPoints = new HashSet<>();
for(int i = point.y-1; i >= 0; i--){
if(board[point.x][i] == 6)
break;
if(board[point.x][i] == 0)
addedPoints.add(new Point(point.x, i));
}
return addedPoints;
}
static void markPoints(Set<Point> points){
for (Point point : points) {
board[point.x][point.y] = -1;
}
}
static void revertPoints(Set<Point> points){
for (Point point : points) {
board[point.x][point.y] = 0;
}
}
static int countHiddenPoints(){
int count = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if(board[i][j] ==0) {
count++;
}
}
}
return count;
}
static class CCTV{
Point point;
int type;
public CCTV(Point point, int type){
this.point = point;
this.type = type;
}
}
static class Point{
int x,y;
public Point(int x, int y){
this.x=x;
this.y=y;
}
}
}