BaekJoon
[BaekJoon] 15683번 감시 (Java) 문제 풀이 [Gold 3]
Tenacity_Dev
2025. 2. 27. 14:25
728x90
문제
https://www.acmicpc.net/problem/15683
어떻게 풀 것인가?
CCTV 방향 탐색을 위한 순열 생성
- permutation(0, cctvList.size())을 호출하여 DFS 기반 순열 생성을 수행한다.
- 각 CCTV는 최대 4가지 방향을 가질 수 있으므로, 모든 CCTV의 가능한 방향 조합을 만들어서 하나씩 테스트한다.
- 모든 CCTV에 대해 방향이 결정되면, 원본 map을 copyMap에 복사한 후, 해당 조합으로 CCTV 감시를 수행한다.
CCTV 감시 영역 설정
- direction(CCTV cctv, int d) 함수는 CCTV의 종류에 따라 감시 방향을 설정하는 역할을 한다.
- 각 CCTV는 다음과 같이 감시할 방향을 결정한다:
- 1번: 한 방향 감시
- 2번: 서로 반대 방향 두 곳 감시
- 3번: 직각 방향 두 곳 감시
- 4번: 세 방향 감시
- 5번: 네 방향 모두 감시
- 결정된 방향에 따라 watch(CCTV cctv, int d)를 호출해 감시 영역을 설정한다.
감시 영역 설정 (BFS)
- watch(CCTV cctv, int d) 함수는 BFS(너비 우선 탐색) 방식으로 CCTV의 감시 영역을 설정한다.
- 특정 방향 d로 이동하면서 벽(6)을 만나기 전까지 감시 영역을 확장한다.
- 감시된 공간을 -1로 표시하여 구분한다.
- 이미 감시된 공간이거나 다른 CCTV가 있는 경우, 계속 탐색을 진행한다.
사각지대 계산 및 최소값 갱신
- getBlindSpot() 함수는 copyMap에서 0(감시되지 않은 영역)의 개수를 세어 사각지대 크기를 구한다.
- 사각지대가 가장 작은 값을 유지하도록 answer 변수를 갱신한다.
풀면서 놓쳤던점
생각보다 고려해야할 것들이 너무 많았다..
이 문제를 통해 얻어갈 것
- 백트래킹(DFS): CCTV의 모든 방향 조합을 생성하여 탐색.
- BFS: CCTV의 감시 영역을 확장.
- 브루트포스(완전 탐색): 모든 경우를 고려하여 최적해 탐색.
- 맵 복사 및 시뮬레이션: 기존 맵을 유지하면서 다양한 경우의 수를 테스트.
내 코드
import java.io.*;
import java.util.*;
public class Main {
private static int N, M;
private static int[][] map;
private static int[][] copyMap;
private static int[] output;
private static ArrayList<CCTV> cctvList;
private static int[] dx = { -1, 0, 1, 0 }; // 상 우 하 좌 시계방향 순서
private static int[] dy = { 0, 1, 0, -1 };
private static int answer = Integer.MAX_VALUE;
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());
map = 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++) {
map[i][j] = Integer.parseInt(st.nextToken());
if (map[i][j] != 0 && map[i][j] != 6) {
cctvList.add(new CCTV(map[i][j], i, j));
}
}
}
output = new int[cctvList.size()]; // 순열을 담을 배열
permutation(0, cctvList.size());
System.out.println(answer);
}
// DFS로 상하좌우 4방향 중에서 cctv의 총 개수, r만큼을 순서대로 뽑는 순열을 구함
public static void permutation(int depth, int r) {
if (depth == r) {
// Copy original 'map' array
copyMap = new int[N][M];
for (int i = 0; i < map.length; i++) {
System.arraycopy(map[i], 0, copyMap[i], 0, map[i].length);
}
// cctv번호와 순열로 뽑혀진 방향에 맞는 상하좌우 방향 설정
for (int i = 0; i < cctvList.size(); i++) {
direction(cctvList.get(i), output[i]);
}
// 사각 지대 구하기
findBlindSpot();
return;
}
for (int i = 0; i < 4; i++) {
output[depth] = i;
permutation(depth + 1, r);
}
}
// 각 cctv 번호와 순열로 뽑혀진 방향에 맞게 감시
private static void direction(CCTV cctv, int d) {
int cctvNum = cctv.num;
if (cctvNum == 1) {
if (d == 0)
spyCCTV(cctv, 0);
else if (d == 1)
spyCCTV(cctv, 1);
else if (d == 2)
spyCCTV(cctv, 2);
else if (d == 3)
spyCCTV(cctv, 3);
} else if (cctvNum == 2) {
if (d == 0 || d == 2) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 2);
} else {
spyCCTV(cctv, 1);
spyCCTV(cctv, 3);
}
} else if (cctvNum == 3) {
if (d == 0) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 1);
} else if (d == 1) {
spyCCTV(cctv, 1);
spyCCTV(cctv, 2);
} else if (d == 2) {
spyCCTV(cctv, 2);
spyCCTV(cctv, 3);
} else if (d == 3) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 3);
}
} else if (cctvNum == 4) {
if (d == 0) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 1);
spyCCTV(cctv, 3);
} else if (d == 1) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 1);
spyCCTV(cctv, 2);
} else if (d == 2) {
spyCCTV(cctv, 1);
spyCCTV(cctv, 2);
spyCCTV(cctv, 3);
} else if (d == 3) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 2);
spyCCTV(cctv, 3);
}
} else if (cctvNum == 5) {
spyCCTV(cctv, 0);
spyCCTV(cctv, 1);
spyCCTV(cctv, 2);
spyCCTV(cctv, 3);
}
}
// BFS로 방향에 맞게 감시
private static void spyCCTV(CCTV cctv, int d) {
Queue<CCTV> queue = new LinkedList<>();
boolean[][] visited = new boolean[N][M];
queue.add(cctv);
visited[cctv.x][cctv.y] = true;
while (!queue.isEmpty()) {
int nx = queue.peek().x + dx[d];
int ny = queue.poll().y + dy[d];
// 범위를 벗어나거나 벽을 만나면 끝
if (!range(nx, ny) || copyMap[nx][ny] == 6) {
break;
}
if (copyMap[nx][ny] == 0) {
copyMap[nx][ny] = -1; // 빈칸이라면 감시할 수 있다는 의미로 -1
queue.add(new CCTV(cctv.num, nx, ny));
} else { // 다른 cctv가 있거나 이미 감시된 칸이라면 그냥 통과
queue.add(new CCTV(cctv.num, nx, ny));
}
}
}
private static boolean range(int x, int y) {
return 0 <= x && x < N && 0 <= y && y < M;
}
// 사각 지대 구하기
private static void findBlindSpot() {
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);
}
}
class CCTV {
int num;
int x;
int y;
CCTV(int num, int x, int y) {
this.num = num;
this.x = x;
this.y = y;
}
}
참고
x
728x90