문제주소 :www.acmicpc.net/problem/2206
<문제 설명>
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을 출력한다.
<풀이법>
▒ 한줄 개념: (벽을 부쉈을 때, 벽을 안 부쉈을 때) 2가지 경우의 거리 배열 ▒
BFS로 문제를 푸는 것은 동일하나, 하나의 distance 배열을 이용해서 distance를 계산하는 것이 아니라 벽을 부쉈을 때와 부수지 않았을 때를 나눠서 distance를 계산해야합니다.
왜냐하면 벽을 부쉈을 때를 최단 거리로 해서 가다가 또 다른 벽을 만나게 될 경우, 해당 경로는 막힌 경로가 됩니다.
이 때 좀 돌아오더라도 벽을 부수지 않고 왔던 경로가 있다면, 다시 경로를 탐색할 수 있게 됩니다.
이번 문제는 테스트 케이스가 부실하여 여러가지 반례가 질문 게시판에 있었고, 이 반례들을 이용하여 문제들을 풀 수 있었습니다.
가장 고생했던 부분은 11%에서 틀렸습니다가 계속 나오는 것이었는데, 모든 반례를 풀어도 해결할 수 없었습니다.
생각보다 간단한 부분에서 문제가 있었는데, 경로의 디폴트 값을 100000으로 해주었던 것이 문제였습니다.
11%의 테스트 케이스에서는 경로가 100000인 결과가 나오는 것 같습니다.
따라서 혹여나 11%에서 틀리시는 분들은 경로의 최댓값 및 디폴트 값 초기화 부분을 확인해보시는 것을 추천드립니다.
<사용한 반례들>
4 4
0111
1111
1111
1110
> -1
8 8
01000100
01010100
01010100
01010100
01010100
01010100
01010100
00010100
> 29
3 6
010000
010111
000110
>12
5 5
00000
11110
00000
01111
00010
>9
5 4
0001
1101
0001
0111
0010
>12
10 10
0111011011
1010011011
1000100011
1000101100
0011010000
1101110101
0110111101
1010100100
1111001011
0001010100
>-1
2 4
0110
0010
>5
<코드(Java)>
package Baekjoon;
import java.util.Scanner;
import java.util.LinkedList;
public class Breaking_Wall_and_Moving_2206 {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int M = sc.nextInt();
int size = N*M;
int[] array = new int[size];
int[] distance_plus = new int[size];
int[] distance_minus = new int[size];
LinkedList<Integer> queue = new LinkedList<>();
int i = 0;
for(int n = 0; n<N; n++){
String temp = sc.next();
for(int m = 0; m<temp.length(); m++){
array[i] = Character.getNumericValue(temp.charAt(m));
distance_plus[i] = 999999;
distance_minus[i] = 999999;
i++;
}
}
queue.offer(0);
int count = 0;
while(!queue.isEmpty()){
int queue_size = queue.size();
count += 1;
for(int q = 0; q < queue_size; q++) {
int popped = queue.poll();
int popped_check_1 = (popped < 0) ? -1 : 1;
int popped_value = Math.abs(popped);
boolean check = false;
if (popped_check_1 >= 0 && count < distance_plus[popped_value]) {
distance_plus[popped_value] = count;
check = true;
}
else if(popped_check_1 < 0 && count < distance_minus[popped_value]){
distance_minus[popped_value] = count;
check = true;
}
if(check){
{
if ((popped_value + 1) % M != 0) {
if (array[popped_value + 1] == 0)
queue.offer(popped_check_1 * (popped_value + 1));
else if (array[popped_value + 1] == 1 && popped_check_1 > 0)
queue.offer((popped_value + 1) * -1);
}
if (popped_value != 0 && (popped_value - 1) % M != M - 1) {
if (array[popped_value - 1] == 0)
queue.offer(popped_check_1 * (popped_value - 1));
else if (array[popped_value - 1] == 1 && popped_check_1 > 0)
queue.offer((popped_value - 1) * -1);
}
if (popped_value + M < size) {
if (array[popped_value + M] == 0)
queue.offer(popped_check_1 * (popped_value + M));
else if (array[popped_value + M] == 1 && popped_check_1 > 0)
queue.offer((popped_value + M) * -1);
}
if (popped_value - M >= 0) {
if (array[popped_value - M] == 0)
queue.offer(popped_check_1 * (popped_value - M));
else if (array[popped_value - M] == 1 && popped_check_1 > 0)
queue.offer((popped_value - M) * -1);
}
}
}
}
}
int answer = (distance_minus[size - 1] > distance_plus[size - 1]) ? distance_plus[size - 1] : distance_minus[size - 1];
if(answer == 999999)
System.out.println(-1);
else
System.out.println(answer);
}
}
더 많은 코드 보기(GitHub) : github.com/dwkim-97/CodingTest
'Baekjoon' 카테고리의 다른 글
[백준15650] N과 M (2) / Java (0) | 2021.01.05 |
---|---|
[백준15649] N과 M (1) / Java (0) | 2021.01.05 |
[백준2178] 숨바꼭질 / Java (0) | 2020.12.30 |
[백준7569] 토마토(3차원) / Java (0) | 2020.12.30 |
[백준7578] 토마토 / Java (0) | 2020.12.29 |