일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 탐색
- DP
- 완전탐색
- 배열
- 새벽코딩
- Queue
- dfs
- 알고리즘
- 백준
- Java
- Stack
- BufferedReader
- HashMap
- 시뮬레이션
- 문자열
- Python
- oracle
- 다이나믹프로그래밍
- 구현
- 그리디
- 아스키코드
- 빅데이터
- 다리 만들기
- BFS
- 백트래킹
- 프로그래머스
- LIS
- 브루트포스
- 스택
- SQL
- Today
- Total
새벽코딩
[백준] [15683] 감시 (JAVA) (구현) (백트래킹) 본문
https://www.acmicpc.net/problem/15683
15683번: 감시
스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감
www.acmicpc.net

1 초 | 512 MB | 46665 | 22179 | 13439 | 44.298% |
문제
스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감시할 수 있는 방법은 다음과 같다.
1번 | 2번 | 3번 | 4번 | 5번 |
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
위의 예시에서 감시할 수 있는 방향을 알아보면 아래와 같다.
왼쪽 상단 2: ↔, 오른쪽 하단 2: ↔ | 왼쪽 상단 2: ↔, 오른쪽 하단 2: ↕ | 왼쪽 상단 2: ↕, 오른쪽 하단 2: ↔ | 왼쪽 상단 2: ↕, 오른쪽 하단 2: ↕ |
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개를 넘지 않는다.
※ JAVA 코드(감시)
import java.io.*;
import java.util.*;
public class Main {
private static int N, M;
private static int[][] map;
private static int result = 64;
private static ArrayList<Cctv> cctvList;
private static ArrayList<Boolean> visitCctv;
public static void main(String [] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = null;
int num;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
map = new int[N][M];
cctvList = new ArrayList<>();
visitCctv = new ArrayList<>();
// 맵 입력
for(int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for(int j = 0; j < M; j++) {
num = Integer.parseInt(st.nextToken());
map[i][j] = num;
// cctv 유형과 위치를 리스트에 담는다
if(num >= 1 && num <= 5) {
Cctv cctv = new Cctv(num, i, j);
cctvList.add(cctv);
visitCctv.add(false);
}
}
}
chkDirection(0, map);
System.out.println(result);
}
static void chkDirection(int idx, int[][] copyMap) {
if(idx == cctvList.size()) {
int cnt = 0;
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
if(copyMap[i][j] == 0) {
cnt++;
}
}
}
// 사각지대가 최소인 값
result = Math.min(result, cnt);
return;
}
int[][] newMap = new int[N][M];
for(int n = 0; n < N; n++) {
for(int m = 0; m < M; m++) {
newMap[n][m] = copyMap[n][m];
}
}
int ccType = cctvList.get(idx).type;
int[] dx;
int[] dy;
// cctv type check
if(!visitCctv.get(idx)){
int[][] dXX = {};
int[][] dYY = {};
// cctv 타입별 감시 방향
switch (ccType) {
case 1:
dXX = new int[][] {{0}, {1}, {0}, {-1}};
dYY = new int[][] {{-1}, {0}, {1}, {0}};
break;
case 2:
dXX = new int[][] {{-1, 1}, {0, 0}};
dYY = new int[][] {{0, 0}, {-1, 1}};
break;
case 3:
dXX = new int[][] {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
dYY = new int[][] {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
break;
case 4:
dXX = new int[][] {{-1, 0, 1}, {0, 1, 0}, {1, 0, -1}, {0, -1, 0}};
dYY = new int[][] {{0, -1, 0}, {-1, 0, 1}, {0, 1, 0}, {1, 0, -1}};
break;
case 5:
dXX = new int[][] {{0, 1, 0, -1}};
dYY = new int[][] {{-1, 0, 1, 0}};
break;
}
for(int i = 0; i < dXX.length; i++) {
dx = dXX[i];
dy = dYY[i];
for(int n = 0; n < N; n++) {
for(int m = 0; m < M; m++) {
newMap[n][m] = copyMap[n][m];
}
}
visitCctv.set(idx, true);
for(int u = 0; u < dx.length; u++) {
int k = 1;
while(true) {
int nextX = cctvList.get(idx).x + dx[u] * k;
int nextY = cctvList.get(idx).y + dy[u] * k;
if(nextX < 0 || nextX >= N || nextY < 0 || nextY >= M || map[nextX][nextY] == 6) {
break;
} else {
if(newMap[nextX][nextY] == 0) {
// 감시중
newMap[nextX][nextY] = 7;
}
}
k++;
}
}
chkDirection(idx + 1, newMap);
for(int n = 0; n < N; n++) {
for(int m = 0; m < M; m++) {
newMap[n][m] = copyMap[n][m];
}
}
visitCctv.set(idx, false);
}
}
}
}
※ 생각정리 (감시)
삼성 A형 기출문제라 확실히 좀 난이도가 있었던 문제이다.
먼저 1 ~ 5번 시시티비의 각 감시 좌표를 배열에 담아두었다.
switch (ccType) {
case 1:
dXX = new int[][] {{0}, {1}, {0}, {-1}};
dYY = new int[][] {{-1}, {0}, {1}, {0}};
break;
case 2:
dXX = new int[][] {{-1, 1}, {0, 0}};
dYY = new int[][] {{0, 0}, {-1, 1}};
break;
case 3:
dXX = new int[][] {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
dYY = new int[][] {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
break;
case 4:
dXX = new int[][] {{-1, 0, 1}, {0, 1, 0}, {1, 0, -1}, {0, -1, 0}};
dYY = new int[][] {{0, -1, 0}, {-1, 0, 1}, {0, 1, 0}, {1, 0, -1}};
break;
case 5:
dXX = new int[][] {{0, 1, 0, -1}};
dYY = new int[][] {{-1, 0, 1, 0}};
break;
}
여기서 dXX의 길이만큼 반복해서 탐색을 하니 한번에 감시할수 있는 방향을 묶어서 탐색할 수 있다.
이후로 각 탐색방향으로 맵의 범위를 벗어나지 않고 벽을 만나기 전까지 빈공간(0)을 감시중인 공간(7)로 바꾸어 주며 탐색을 해나갔고 해당 재귀가 끝나면 사전에 담아주었던 임시 맵의 값으로 다시 맵을 채워넣어주어 초기화 하였다.
자세한 설명은 소스에 주석을 달아 두겠습니다.
// 각 시시티비 유형별로 반복
for(int i = 0; i < dXX.length; i++) {
// dx : 현재좌표에서 x좌표평면 어디방향으로
// dy : 현재좌표에서 y좌표평면 어디방향으로
dx = dXX[i];
dy = dYY[i];
// 각 시시티비 유형안에서도 방향을 1개 ~ 4개 방향으로 묶을 수 있다.
// 각 방향별로 모두 같은 조건에서 탐색해야하기 때문에 copyMap의 값을 가져와 초기화
for(int n = 0; n < N; n++) {
for(int m = 0; m < M; m++) {
newMap[n][m] = copyMap[n][m];
}
}
// 현재 인덱스를 방문했는지 체크
visitCctv.set(idx, true);
for(int u = 0; u < dx.length; u++) {
int k = 1;
// 무한루프
while(true) {
// 현재좌표 + (가야할 방향 * 방향 횟수(반복증가))
int nextX = cctvList.get(idx).x + dx[u] * k;
int nextY = cctvList.get(idx).y + dy[u] * k;
// 루프탈출 조건은 맵의 끝이거나 벽을 만날때
if(nextX < 0 || nextX >= N || nextY < 0 || nextY >= M || map[nextX][nextY] == 6) {
break;
} else { // 위 조건을 만족하지 않는 경우 0을 7로 변경해둔다 (이는 임시로 바꾼것)
if(newMap[nextX][nextY] == 0) {
// 감시중
newMap[nextX][nextY] = 7;
}
}
k++;
}
}
// 해당재귀를 유지하고 다음 인덱스로 재귀를 태운다 이때 맵을 같이 태워간다
chkDirection(idx + 1, newMap);
// 해당 재귀를 다음 인덱스로 보냈으니 다시 맵을 초기화 해준다
for(int n = 0; n < N; n++) {
for(int m = 0; m < M; m++) {
newMap[n][m] = copyMap[n][m];
}
}
// 방문체크 해제 다음 재귀에서 반복문에 들어올 수 있게 체크 해제
visitCctv.set(idx, false);
}
감사합니다.
질문은 댓글 주세요
- 새벽코딩 -
'알고리즘' 카테고리의 다른 글
[프로그래머스] 할인행사 (JAVA) (1) | 2023.12.02 |
---|---|
[백준] [17135] 캐슬디펜스 (JAVA) (백트래킹) (구현) (0) | 2023.11.19 |
[백준] [14500] 테트로미노 (JAVA) (구현) (0) | 2023.11.16 |
[백준] [14889] 스타트와 링크 (JAVA) (백트래킹) (0) | 2023.11.08 |
[백준] [14888] 연산자 끼워넣기 (JAVA) (백트래킹) (0) | 2023.11.07 |