📌 문제 링크 - https://www.acmicpc.net/problem/25307
문제
시루는 부모님과 함께 백화점에 갔다. 부모님은 쇼핑할 것이 많기 때문에 여러 곳을 돌아다녀야 하고, 시루는 부모님과 함께 걸어다니는 것이 너무 힘들어서 의자에 앉아서 쉬려고 한다.
백화점은 세로 길이가 N, 가로 길이가 M인 격자 형태이고, 상하좌우로 인접한 칸으로 이동할 때마다 1 만큼의 체력을 소모한다. 시루는 현재 위치에서 출발해 백화점 곳곳에 있는 의자 중 하나를 찾아가서 앉으려고 한다. 시루는 백화점 밖으로 나가면 부모님께 혼나기 때문에 백화점 밖으로 나갈 수 없다.
백화점에는 건물을 지탱하기 위한 기둥과 옷을 전시하기 위한 마네킹이 있다. 시루는 기둥이 있는 칸으로 이동하지 못하고, 마네킹을 무서워하기 때문에 마네킹과 거리가 K 이하인 칸은 사용하지 않으려고 한다. 이때 두 칸 (rx, cx),(ry, cy)의 거리는 |rx − ry| + |cx − cy| 이다. 단, 시루가 출발할 때는 부모님과 함께 있기 때문에, 출발 지점과 마네킹과의 거리가 K 이하가 되어도 출발할 수 있다.
시루는 다리가 너무 아프기 때문에 체력 소모를 최소화하면서 의자까지 찾아가려고 한다. 시루가 소모하는 체력의 최솟값을 구해보자.
입력
첫째 줄에 백화점의 세로 길이, 가로 길이, 마네킹과 떨어져야 하는 거리를 의미하는 정수 N, M, K가 공백으로 구분되어 주어진다. (1 ≤ N, M ≤ 2000, 0 ≤ K ≤ 4000)
둘째 줄부터 N개의 줄에 각각 M개씩 위에서부터 차례로 백화점의 상태가 주어진다. 아무것도 없는 칸은 0, 기둥이 있는 칸은 1, 의자가 있는 칸은 2, 마네킹이 있는 칸은 3, 시루의 시작 위치는 4로 주어진다. 시루의 시작 위치는 정확히 한 번 주어지고, 해당 위치에 기둥, 의자, 마네킹이 존재하지 않는다.
출력
시루가 의자를 찾아갈 수 있다면 시루가 소모하는 체력의 최솟값을 출력한다.
만약 의자를 찾아갈 수 없다면 -1을 출력한다.
풀이
마네킹과의 거리가 K 이하인 칸들을 미리 bfs로 구해서 방문 불가능하게 체크해 준 후, 시루가 최소한의 거리로 의자를 찾을 수 있는지 다시 bfs로 확인하면 된다.
간단한 문제였는데 맨 처음에 마네킹 거리를 체크하는 과정에서 기둥인 경우 넘어가게 하는 실수를 해서 계속 오답이 나왔었다. 마네킹 거리를 체크할 때는 기둥 너머까지 체크를 해야 하므로 기둥도 q에 담아 가며 체크를 해주어야 하고,
의자를 찾는 bfs에서는 기둥을 넘어가도록 따로 체크해 주면 된다!!
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
// https://www.acmicpc.net/problem/25307
public class Main_25307 {
private static final int WALL = 1, TARGET = 2, MANNEQUIN = 3;
private static final int[] dr = {-1, 1, 0, 0};
private static final int[] dc = {0, 0, -1, 1};
private static int N, M; // 백화점의 세로, 가로
private static int sr, sc; // 시루의 시작 위치
private static int[][] map; // 백화점의 상태
private static boolean[][] visited; // 방문 체크
public static void main(String[] args) throws IOException {
init();
int min = bfs();
System.out.print(min);
}//main
private static int bfs() {
Queue<int[]> q = new LinkedList<>();
int r, c, power;
q.offer(new int[] {sr, sc, 0});
visited[sr][sc] = true;
int[] cur;
while(!q.isEmpty()) {
cur = q.poll();
r = cur[0]; // 행
c = cur[1]; // 열
power = cur[2]; // 체력
// 의자 도착
if(map[r][c] == TARGET) {
return power;
}
for(int i=0; i<4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
// 범위, 방문 여부, 벽 체크
if(isOutOfRange(nr, nc) || visited[nr][nc] || map[nr][nc] == WALL) continue;
visited[nr][nc] = true;
q.offer(new int[] {nr, nc, power + 1});
}
}
return -1;
}//bfs
private static boolean isOutOfRange(int r, int c) {
return r < 0 || r >= N || c < 0 || c >= M;
}//isOut
private static void distCheck(Queue<int[]> q) {
int r, c, d;
int[] cur;
while(!q.isEmpty()) {
cur = q.poll();
r = cur[0]; // 행
c = cur[1]; // 열
d = cur[2]; // 거리
if(d == 0) continue;
for(int i=0; i<4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
// 범위, 방문 여부 체크
if(isOutOfRange(nr, nc) || visited[nr][nc]) continue;
visited[nr][nc] = true;
q.offer(new int[] {nr, nc, d - 1});
}
}
}//distCheck
private static void init() 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()); // 백화점의 가로 길이
int K = Integer.parseInt(st.nextToken()); // 마네킹과 떨어져야 하는 거리
map = new int[N][M]; // 백화점의 상태
visited = new boolean[N][M]; // 방문 체크
Queue<int[]> q = new LinkedList<>();
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] == MANNEQUIN) {
visited[i][j] = true;
q.offer(new int[] {i, j, K});
}
// 시루의 시작 위치
else if(map[i][j] == 4) {
sr = i;
sc = j;
}
}
}
distCheck(q); // 마네킹 거리 체크
br.close();
}//init
}//class
'Algorithm > 백준' 카테고리의 다른 글
[Java] 백준 14907. 프로젝트 스케줄링 (0) | 2025.04.02 |
---|---|
[Java] 백준 1035. 조각 움직이기 (0) | 2025.03.29 |
[JAVA] 백준 14948. 군대탈출하기 (0) | 2025.03.21 |
[Java] 백준 2758. 로또 (0) | 2025.03.10 |
[JAVA] 백준 4198. 열차정렬 (0) | 2025.02.20 |