삼성기출 | BOJ 백준 15683번 감시 | Java
🔎 문제 설명
💛 골드 3
- 난이도 ★★☆☆ - 구현/시뮬레이션
백준 15683번 문제는 삼성 기출 문제로, 삼성기출 단골 유형인 시뮬레이션으로 푸는 문제다. 일단 문제 길이가 엄청나서 놓치는 부분 없이 잘 이해하는 것이 중요하다.
감시카메라가 회전을 할 수 있다는 점이 이 문제를 복잡하게 만드는데, 여기서 주목해야하는 점은 N과 M의 범위가 상당히 작고 감시카메라의 개수도 적다는 점이다. 이러면 시간적 제약에서 벗어나 순열 을 적용할 수 있다.
어떻게 순열을 사용한다는 거야?
감시카메라가 회전할 수 있는 방향은 위에서 봤을 때 상하좌우 총 4개이므로, 각각의 CCTV가 어느 방향으로 회전할지 순열로 미리 정해놓고 이를 시뮬레이션하면서 사각지대를 찾는다. 조합이 아닌 이유는 감시카메라가 1~5번까지 존재하는데 중복되는 카메라가 있을 수 있기 때문이다.
문제에서 헷갈릴 만한 포인트들을 정리해서 남길테니 혹시라도 이 글을 찾아본 다른 사람에게 도움이 되었으면 좋겠다!
💡 문제 이해 포인트
- CCTV는 회전시킬 수 있는데, 회전은 항상 90도 방향으로 해야 하며, 감시하려고 하는 방향이 가로 또는 세로 방향이어야 한다.
- CCTV는 벽을 통과할 수 없다.
- CCTV는 CCTV를 통과할 수 있다.
추가로 내가 문제 풀이에 도움이 되었던 반례를 아래에 코드와 함께 첨부하였다. 나 같은 경우는 5번 감시카메라가 회전할 필요 없어서 따로 처리했었는데, 이 부분에서 오류가 있었다.
내가 풀었던 문제 중에서 문제가 엄청 긴 편에 속한다. 코드를 더 줄이거나 백트래킹을 적용할 수 있는 방법을 좀 더 모색해봐야겠다.
💻 내 코드
public class Main_15683_G3_감시 {
static final int INF = Integer.MAX_VALUE;
static int N, M, answer, directions[];
static char board[][], copyMap[][];
static List<CCTV> cctvList;
public static void main(String[] args) throws Exception {
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 char[N][M];
cctvList = new ArrayList<>();
answer = INF;
for (int i=0; i<N; i++) {
st = new StringTokenizer(br.readLine());
for (int j=0; j<M;j ++) {
board[i][j] = st.nextToken().charAt(0);
if (board[i][j] != '0' && board[i][j] != '6' && board[i][j] != '5')
cctvList.add(new CCTV(i, j, board[i][j]-'0'));
}
}
// 5번은 회전할 필요 없음
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
if (board[i][j]=='5') cctv5(i, j);
}
}
directions = new int[cctvList.size()];
permutation(0);
System.out.println(answer);
}
private static void permutation(int depth) {
if (depth == cctvList.size()) {
copyMap = new char[N][M];
for (int i=0; i<N; i++) copyMap[i] = Arrays.copyOf(board[i], M);
for (int i=0; i<cctvList.size(); i++) {
setDirection(cctvList.get(i), directions[i]);
}
countBlind();
return;
}
for (int i=0; i<4; i++) {
if (cctvList.get(depth).no == 5) directions[depth] = 0;
else directions[depth] = i;
permutation(depth+1);
}
}
private static void cctv5 (int x, int y) {
for (int i=0; i<N-x; i++) {
if (board[x+i][y] == '6') break;
if (board[x+i][y] == '0') board[x+i][y] = '#';
}
for (int i=0; i<=x; i++) {
if (board[x-i][y] == '6') break;
if (board[x-i][y] == '0') board[x-i][y] = '#';
}
for (int i=0; i<M-y; i++) {
if (board[x][y+i] == '6') break;
if (board[x][y+i] == '0') board[x][y+i] = '#';
}
for (int i=0; i<=y; i++) {
if (board[x][y-i] == '6') break;
if (board[x][y-i] == '0') board[x][y-i] = '#';
}
}
// 상우하좌(시계방향)
static int[] dx = {-1,0,1,0};
static int[] dy = {0,1,0,-1};
// BFS
private static void watch(CCTV cctv, int dir) {
Queue<CCTV> q = new LinkedList<>();
q.add(cctv);
while (!q.isEmpty()) {
CCTV now = q.poll();
int nx = now.x + dx[dir], ny = now.y + dy[dir];
if (0>nx || nx>=N || 0>ny || ny>=M || copyMap[nx][ny] == '6') break;
if (copyMap[nx][ny]=='0') {
copyMap[nx][ny] = '#';
q.add(new CCTV(nx, ny, cctv.no));
} else {
q.add(new CCTV(nx, ny, cctv.no));
}
}
}
// 2번은 2 방향만, 1,3,4는 4방향 다 고려
private static void setDirection(CCTV cctv, int dir) {
if (cctv.no == 2) {
if (dir % 2 == 0) {
watch(cctv, 0); watch(cctv, 2);
} else {
watch(cctv, 1); watch(cctv, 3);
}
} else if (cctv.no == 1) {
watch(cctv, dir);
} else if (cctv.no == 3) {
watch(cctv, dir);
watch(cctv, (dir+1)%4);
} else {
watch(cctv, dir);
watch(cctv, (3+dir)%4);
watch(cctv, (dir+1)%4);
}
}
private static void countBlind() {
int cnt = 0;
for (int i=0; i<N; i++) {
for (int j = 0; j < M; j++) {
if (copyMap[i][j] == '0') cnt++;
}
}
answer = Math.min(answer, cnt);
}
static class CCTV {
int x, y, no;
public CCTV(int x, int y, int no) {
this.x = x;
this.y = y;
this.no = no;
}
}
}
🔫 반례
질문 게시판을 참고했다.
5 2
0 0
0 0
3 0
0 0
6 0
answer) 5
5 5
0 0 0 0 0
6 2 0 0 4
0 0 0 4 0
5 0 0 5 0
6 0 0 0 0
answer) 3
아래 코드는 회전하는 로직을 제대로 짰는지 확인할 때 쓰면 편하다.
3 3
0 0 0
0 1 0
0 0 0
ans) 7
3 3
0 0 0
0 2 0
0 0 0
ans)6
3 3
0 0 0
0 3 0
0 0 0
ans)6
3 3
0 0 0
0 4 0
0 0 0
ans)5
3 3
0 0 0
0 5 0
0 0 0
ans)4
💙 You need to log in to GitHub to write comments. 💙
If you can't see comments, please refresh page(F5).