문제 링크 : https://www.acmicpc.net/problem/15683
백트래킹 문제였습니다.
- CCTV 클래스를 만들기. CCTV는 위치정보와 CCTV타입을 가지고 있음.
- cctvList를 만들어 CCTV들을 저장
- 경우의 수를 모두 구해 사각지대 최솟값 도출
문제풀이 초기 설정을 위와 같이 잡고 문제를 풀었습니다.
코드가 상당히 길게 나왔는데 문제의 핵심은 dfs메서드의 이 부분입니다.
CCTV now = cctvList.get(depth);
// cctv의 타입에 따라
switch (now.type) {
case 1:
// dir은 방향, 1번 유형은 4개의 경우의 수
for (int dir = 0; dir < 4; dir++) {
// map의 정보를 copy에 복사해놓고
copy(map, copy);
move(now, dir);
dfs(depth + 1);
// 원상복구 시킴
copy(copy, map);
}
break;
case 2:
// 2번 유형은 2개의 경우의 수
for (int dir = 0; dir < 2; dir++) {
copy(map, copy);
move(now, dir);
move(now, (dir + 2) % 4);
dfs(depth + 1);
copy(copy, map);
}
break;
case 3:
for (int dir = 0; dir < 4; dir++) {
copy(map, copy);
move(now, dir);
move(now, (dir + 1) % 4);
dfs(depth + 1);
copy(copy, map);
}
break;
case 4:
for (int dir = 0; dir < 4; dir++) {
copy(map, copy);
move(now, dir);
move(now, (dir + 1) % 4);
move(now, (dir + 2) % 4);
dfs(depth + 1);
copy(copy, map);
}
break;
case 5:
// 5번 유형은 4방향 다 감시하므로 하나의 경우의 수 밖에 없음
copy(map, copy);
// 1방향씩 네번 반복하니까 4방향 다 본다는 의미, 일일이 move(now, dir+1), move(now, dir+2)... 해도 상관 X
for (int dir = 0; dir < 4; dir++) {
move(now, dir);
}
dfs(depth + 1);
copy(copy, map);
break;
}
주석을 달아놨지만 추가적으로 설명을 해보겠습니다.
switch문은 cctv의 유형에 따라 작동합니다.
copy는 깊은복사 메서드입니다. 각 경우의 수를 살펴보려면 원본 map과 변화한 map을 가지고 있어야 합니다. copy메서드는 이를 위한 백업이라고 생각하면 편합니다.
1. copy로 map 배열을 copy 배열에 백업
2. move로 맵에 변화를 주고 dfs(depth+1)에서 넘김
3. 백업해놓은 copy를 map으로 원상복구
전체코드
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
class CCTV {
int x;
int y;
int type;
public CCTV(int x, int y, int type) {
this.x = x;
this.y = y;
this.type = type;
}
}
public class Main {
static int N, M;
static int result = Integer.MAX_VALUE;
static int[] dr = {-1, 0, 1, 0};
static int[] dc = {0, -1, 0, 1};
static int[][] map;
static ArrayList<CCTV> cctvList = new ArrayList<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
map = new int[N][M];
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());
// 만약 0이 아니고 6(벽)이 아니면 cctv 이므로 넣어줌
if (map[i][j] != 0 && map[i][j] != 6) {
cctvList.add(new CCTV(i, j, map[i][j]));
}
}
}
dfs(0);
System.out.println(result);
}
static void dfs(int depth) {
// cctv의 갯수
int size = cctvList.size();
if (depth == size) {
// check 메서드로 경우의 수 별 사각지대의 갯수를 구하고 최솟값을 구함
result = Math.min(result, check());
return;
}
// 백업용 copy배열
int[][] copy = new int[N][M];
CCTV now = cctvList.get(depth);
// cctv의 타입에 따라
switch (now.type) {
case 1:
// dir은 방향, 1번 유형은 4개의 경우의 수
for (int dir = 0; dir < 4; dir++) {
// map의 정보를 copy에 복사해놓고
copy(map, copy);
move(now, dir);
dfs(depth + 1);
// 원상복구 시킴
copy(copy, map);
}
break;
case 2:
// 2번 유형은 2개의 경우의 수
for (int dir = 0; dir < 2; dir++) {
copy(map, copy);
move(now, dir);
move(now, (dir + 2) % 4);
dfs(depth + 1);
copy(copy, map);
}
break;
case 3:
for (int dir = 0; dir < 4; dir++) {
copy(map, copy);
move(now, dir);
move(now, (dir + 1) % 4);
dfs(depth + 1);
copy(copy, map);
}
break;
case 4:
for (int dir = 0; dir < 4; dir++) {
copy(map, copy);
move(now, dir);
move(now, (dir + 1) % 4);
move(now, (dir + 2) % 4);
dfs(depth + 1);
copy(copy, map);
}
break;
case 5:
// 5번 유형은 4방향 다 감시하므로 하나의 경우의 수 밖에 없음
copy(map, copy);
// 1방향씩 네번 반복하니까 4방향 다 본다는 의미, 일일이 move(now, dir+1), move(now, dir+2)... 해도 상관 X
for (int dir = 0; dir < 4; dir++) {
move(now, dir);
}
dfs(depth + 1);
copy(copy, map);
break;
}
}
// cctv가 바라보는 부분을 -1로 하는 move 메서드
static void move(CCTV c, int dir) {
int nr = c.x;
int nc = c.y;
while (true) {
nr = nr + dr[dir];
nc = nc + dc[dir];
// map 범위를 벗어나거나 벽을 만낫을때 종료
if (nr < 0 || nc < 0 || nr >= N || nc >= M || map[nr][nc] == 6) {
break;
}
map[nr][nc] = -1;
}
}
// 깊은복사
static void copy(int[][] map, int[][] copy) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
copy[i][j] = map[i][j];
}
}
}
// 0의 갯수 -> 사각지대의 갯수이므로 check 메서드를 통해 각 경우의 수 별 사각지대의 갯수를 구할 수 있음
static int check() {
int cnt = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] == 0) {
cnt++;
}
}
}
return cnt;
}
// 확인용 print
static void print() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
System.out.println();
}
}
많이 부족한 코드입니다. 수정할 점, 잘못된 점, 개선점 보이면 말씀해주세요!!
감사합니다.
'Algorithm > 백준' 카테고리의 다른 글
[JAVA] 백준 10800 컬러볼 (4) | 2023.12.18 |
---|---|
[JAVA] 백준 3020 개똥벌레 (0) | 2023.12.14 |
[JAVA] 백준 10026 적록색약 (2) | 2023.12.03 |