2022. 10. 7. 23:02ㆍ알고리즘
문제
N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.
만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.
한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.
맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.
입력
첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.
출력
첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.
https://www.acmicpc.net/problem/2206
1. 다익스트라로 해결해야 할까 고민했었는데 가중치가 전부 동일한 그래프에서는 bfs를 사용하자.
2. 이 문제에서 어려웠던 부분은, 지금 내 경로가 벽을 부순 경험이 있는지, 없는지 정보를 지니고 있어야 한다는 점이었다.
만약, 인접한 칸이 1이고 현재 나는 벽을 부순 경험이 없다면 --> 벽을 부수고 해당 칸을 큐에 넣은 뒤 bfs를 계속 실행한다.
인접한 칸이 1이고, 내가 이전에 벽을 부순 경험이 있다면 --> 해당 칸으로는 더이상 탐색이 불가능해진다.
단순한 생각으로는 1에서 1로 이동하는 것을 막으면 된다고 생각하겠지만, 1 -> 0 -> 1의 순서로 탐색하는 경우는 문제가 된다. 이미 벽을 부순 경험이 있기 때문이다.
따라서 코드에서는 visit 배열을 삼중 배열로 만들었다. visit[x좌표][y좌표][벽을 부수고 탐색하고 있는지]
예를 들어,
1. 벽이 아니라면(0인 경우)
1) 벽을 부순 경험이 있는가? visit[x][y][1] 방문 처리 후 Point(x,y)를 큐에 넣는다.
2) 벽을 부순 적이 없는가? visit[x][y][0] 방문 처리 후 Point(x,y)를 큐에 넣는다.
2. 벽이라면 (1인 경우)
1) 벽을 부순 경험이 있는가? --> 더이상 탐색이 불가능하다.
2) 벽을 부순 경험이 없는가? --> visit[x][y][1] 방문 처리 후 Point(x,y)를 큐에 넣는다.
또, 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;
public class Main {
static int n, m;
static char[][] map;
static boolean[][][] visit;
static int[] dx = {1, 0, -1, 0};
static int[] dy = {0, 1, 0, -1};
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 char[n][m];
visit = new boolean[n][m][2];
for (int i = 0; i < n; i++) {
String s = br.readLine();
for (int j = 0; j < m; j++) {
map[i][j] = s.charAt(j);
}
}
bfs(new Point(0, 0, 1,false));
}
private static void bfs(Point point) {
Queue<Point> qu = new LinkedList<>();
qu.offer(point);
while (!qu.isEmpty()) {
Point p = qu.poll();
if (p.x == n - 1 && p.y == m - 1) {
//종점인 경우
System.out.println(p.distance);
return;
}
for (int i = 0; i < 4; i++) {
int nx = p.x + dx[i];
int ny = p.y + dy[i];
//맵 밖에 범위인 경우
if (nx < 0 || ny < 0 || nx >= n || ny >= m) {
continue;
}
int nextDistance = p.distance + 1;
if (map[nx][ny] == '1') {
if (!p.destroyed) {
qu.offer(new Point(nx, ny, nextDistance, true));
visit[nx][ny][1] = true;
}
}
else if (map[nx][ny] == '0') {
if(!p.destroyed && !visit[nx][ny][0]){
qu.offer(new Point(nx,ny, nextDistance, false));
visit[nx][ny][0] = true;
}else if(p.destroyed && !visit[nx][ny][1]){
qu.offer(new Point(nx, ny, nextDistance, true));
visit[nx][ny][1] = true;
}
}
}
}
System.out.println(-1);
}
static class Point{
int x;
int y;
int distance;
boolean destroyed;
public Point(int x, int y, int distance, boolean destroyed) {
this.x = x;
this.y = y;
this.distance = distance;
this.destroyed = destroyed;
}
}
}
'알고리즘' 카테고리의 다른 글
백준 1890: 점프 (0) | 2022.11.03 |
---|---|
백준 1520: 내리막길 [Java] - 포포 (0) | 2022.11.01 |
프로그래머스: 더 맵게(lv2) [Java] (0) | 2022.08.30 |
프로그래머스: 뉴스 클러스터링(lv2) [Java] (0) | 2022.08.17 |
프로그래머스: 카카오프렌즈 컬러링북(lv2) (0) | 2022.08.12 |