본문 바로가기
문제 해결/BOJ

[백준] 감시

by 자잘 2023. 2. 21.

https://www.acmicpc.net/problem/15683

 

15683번: 감시

스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감

www.acmicpc.net

정말 다시는 만나고 싶지 않은 문제.. CCTV의 감시구역을 배열로 복사해서 넘기면 상대적으로 간단하게 풀 수 있지만 시간속도나 메모리 사용 여부를 줄여보고자 배열을 복사하지 않고 감시 구역을 체크해주고 DFS에서 돌아오면 다시 복구시키는 방식으로 해결하고자 했습니다. 이런 방식으로 풀 때 주의해야할 실수들은 다음과 같습니다.

 

1) 복구시키는 과정에서 중복으로 감시받는 위치

다른 CCTV에 의해 감시되고 있는 위치인데 현재 CCTV를 복구한다고 복구하다가 여전히 감시되는 위치를 지워버리는 경우가 있습니다. 저는 이 부분을 각각의 감시카메라의 번호만큼 board에서 빼줘서 중복으로 감시받는 위치를 체크해주었습니다.

 

2) dir인덱스 실수

0 ~ 3 인덱스를 사용해야하는데 4 인덱스를 사용하여 실수가 생겼습니다

 

3) CCTV가 없는 경우

CCTV가 없는 경우에는 모든 구역이 위험 구역이므로 체크해줘야합니다.

 

4) 빈 공간이 없는 경우

모든 위치가 벽이거나 CCTV인 경우에는 위험 구역이 없습니다. 

 

 

import java.util.*;
import java.io.*;

public class Main_15683 {
	static int n, m;
	static int[][] board;
	static ArrayList<int[]> cctv;
	static ArrayList<int[]> fives;
	
	static int[] dy = {0, 1, 0, -1};
	static int[] dx = {1, 0, -1, 0};
	
	static int ans = Integer.MAX_VALUE;
	static int count = 0;
	static int[] dirs;
	public static void main(String[] args) throws Exception {
		//System.setIn(new FileInputStream("input.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		
		board = new int[n][m];
		cctv = new ArrayList<>();
		fives = new ArrayList<>();
		
		boolean isBlank = false;
		int cntOfBlank =0;
		for (int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < m; j++) {
				board[i][j] = Integer.parseInt(st.nextToken());
				if (board[i][j] == 0) {
					isBlank = true;
					cntOfBlank++;
				}
				//5번 CCTV는 바로 마킹시켜준다.(회전 필요 X)
				if (board[i][j] == 5) {
					fives.add(new int[] {i, j});
				}
				else if (board[i][j] > 0 && board[i][j] != 6) {
					cctv.add(new int[] {i, j});
				}
			}
		}
		
		if (!isBlank) {
			System.out.println(0);
			return;
		}
		
		if (fives.size() == 0 && cctv.size() == 0) {
			System.out.println(cntOfBlank);
			return;
		}
		
		for (int i = 0; i < fives.size(); i++) {
			int[] cur = fives.get(i);
			rotateFive(cur[0], cur[1]);
		}
		
		dfs(0);
		System.out.println(ans);
	}
	
	static int getAns() {
		int cnt = 0;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (board[i][j] == 0) {
					cnt++;
				}
			}
		}
		
		return cnt;
	}
	
	static void dfs(int cnt) {
		if (cnt == cctv.size()) {
			count++;
			int ret = getAns();
			ans = Math.min(ret, ans);
			return;
		}
		
		int[] cur = cctv.get(cnt);
		
		int y = cur[0];
		int x = cur[1];
		
		int cctvType = board[y][x];
		
		switch(cctvType) {
			case 1:
				rotateOne(y, x, cnt);
				break;
			case 2:
				rotateTwo(y, x, cnt);
				break;
			case 3:
				rotateThree(y, x, cnt);
				break;
			case 4:
				rotateFour(y, x, cnt);
				break;
		}
	}
	
	static boolean inRange(int y, int x) {
		return 0 <= y && y < n && 0 <= x && x < m;
	}
	
	static void marking(int y, int x, int dir, int amount) {
		while(inRange(y, x) && board[y][x] != 6) {
			// CCTV가 아닌 경우
			if (board[y][x] <= 0) {
				board[y][x] -= amount;
			}
			
			y += dy[dir];
			x += dx[dir];
		}
	}
	
	static void restore(int y, int x, int dir, int amount) {
		while(inRange(y, x) && board[y][x] != 6) {
			// CCTV가 아닌 경우
			if (board[y][x] <= 0) {
				board[y][x] += amount;
			}
			
			y += dy[dir];
			x += dx[dir];
		}
	}
	
	static void rotateOne(int startY, int startX, int cnt) {
		for (int dir = 0; dir < 4; dir++) {
			marking(startY + dy[dir], startX + dx[dir], dir, 1);
			dfs(cnt + 1);
			restore(startY + dy[dir], startX + dx[dir], dir, 1);
		}
	}
	
	static void rotateTwo(int startY, int startX, int cnt) {
		int dir = 0;
		int dir2 = 2;
		
		for (int i = 0; i < 2; i++) {
			marking(startY + dy[dir + i], startX + dx[dir + i], dir + i, 2);
			marking(startY + dy[dir2 + i], startX + dx[dir2 + i], dir2 + i, 2);
			
			dfs(cnt + 1);
			
			restore(startY + dy[dir + i], startX + dx[dir + i], dir + i, 2);
			restore(startY + dy[dir2 + i], startX + dx[dir2 + i], dir2 + i, 2);
		}
	}
	
	static void rotateThree(int startY, int startX, int cnt) {
		int dir = 0;
		int dir2 = 3;
		
		for (int i = 0; i < 4; i++) {
			marking(startY + dy[dir + i], startX + dx[dir + i], dir + i, 3);
			marking(startY + dy[(dir2 + i) % 4], startX + dx[(dir2 + i) % 4], (dir2 + i) % 4, 3);
			
			dfs(cnt + 1);
			
			restore(startY + dy[dir + i], startX + dx[dir + i], dir + i, 3);
			restore(startY + dy[(dir2 + i) % 4], startX + dx[(dir2 + i) % 4],(dir2 + i) % 4, 3);
		}
	}	
	
	static void rotateFour(int startY, int startX, int cnt) {
		
		for (int dir = 0; dir < 4; dir++) {
			// dir을 제외한 방향 마킹
			for (int i = 0; i < 4; i++) {
				if (dir == i) {
					continue;
				}
				
				marking(startY + dy[i], startX + dx[i], i, 4);
			}
			
			dfs(cnt + 1);
			
			//dir을 제외한 방향 복구
			for (int i = 0; i < 4; i++) {
				if (dir == i) {
					continue;
				}
				restore(startY + dy[i], startX + dx[i], i, 4);
			}
		}
	}
	
	// 5번은 회전시킬 필요가 없다.
	static void rotateFive(int startY, int startX) {
		for (int i = 0; i < 4; i++) {
			marking(startY + dy[i], startX + dx[i], i, 5);

		}
	}
}

'문제 해결 > BOJ' 카테고리의 다른 글

[백준] 게리 멘더링2  (0) 2023.02.23
[백준] 벽 부수고 이동하기 2  (0) 2023.02.22
[백준] LCA  (0) 2023.02.21
[백준] 당근 훔쳐 먹기  (0) 2023.02.20
[백준] 정복자  (0) 2023.02.19