[java]벽 부수고 이동하기 - 백준2206
문제
풀이
문제 해석을 하자면,
- (1,1) -> (N,M) 좌표로 이동하는 최단 거리를 구하는 문제이다.
- 벽을 1번까지 부수고 지나갈 수 있다는 조건을 해결하는게 핵심이다.
풀이
- 이 문제의 경우 코드 구현은 금방했는데, 해당 아이디어를 생각하는게 좀 오래걸렸다. (기본이 부족 ㅜㅜ)
- 우선 가중치가 동일하므로 다익스트라가 아닌 BFS로 충분히 해결 가능하다.
- 그리고 BFS의 특성을 잘 생각해보면 풀이를 떠올리기 쉽다.
- BFS로 조회하면 해당 레벨에 첫 조회했을때 무조건 최단거리!!
- 즉, 무엇이든 처음 방문이 최단경로(BFS특성)
- 따라서 visited 2개를 사용!! -> 벽O, 벽X 의 경우로 나눈것
- 벽을 부순상태의 경로들을 따로 방문 처리 함으로써 “중복방문 방지”
- 벽을 부수지않은 상태의 경로들을 따로 방문 처리 함으로써 “중복방문 방지”
- 참고) BFS로 조회하는데 “중복방문 방지”는 꼭 고려해줘야 무한처리를 방지!
- 마지막으로 거리는 트리 레벨을 활용!! -> 이것이 “최단거리”가 되는건 자명하다.
코드
import java.io.*;
import java.util.*;
/**
* (1, 1)과 (N, M)은 항상 0 -> 본인은 (0,0)부터 사용하겠다.
* 풀이 : 최단경로 -> 가중치동일 -> BFS
* visited 2개와 트리레벨 활용해보자 -> visited 2개 사용이 핵심!!
* +따로 wall 변수에 벽을 부수었는지 기록해서 사용
* +visited 2개는 벽O, 벽X 의 경우로 나눈것 -> 무엇이든 처음 방문이 최단경로(BFS특성)
* 참고) N==1 && M==1 일 때 벽아니면 이것도 한칸이므로 level 1로 봐야함. (본인 코드는 level 2로 나오게 짜서 따로 예외처리 함)
*/
public class Main {
public static int N,M;
public static int[][] inArr = new int[1005][1005];
public static boolean[][] visitedNoWall = new boolean[1005][1005];
public static boolean[][] visitedYesWall = new boolean[1005][1005];
public static int result1 = Integer.MAX_VALUE;
public static int result2 = Integer.MAX_VALUE;
public static int[] dx = {0,1,0,-1};
public static int[] dy = {1,0,-1,0};
public static void bfs() {
Queue<Pair> qu = new ArrayDeque<>();
qu.offer(new Pair(0,0, 0));
visitedNoWall[0][0] = true;
int level = 1; // (0,0) 부터시작
if(N==1&&M==1) level=0; // 아래에서 어차피 ++하기 때문에 예외처리
while(!qu.isEmpty()) {
//debug
// System.out.println(Arrays.toString(qu.toArray()));
// System.out.println("level:"+level);
int size = qu.size();
level++;
for(int s=0; s<size; s++){
Pair p=qu.poll();
//debug
// if(p.x == 4 && p.y==0) {
// System.out.println("debug");
// }
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;
if(p.wall==0) { // 아직 벽 안부순경우
if(inArr[nx][ny] == 0) {
if(visitedNoWall[nx][ny]) continue;
qu.offer(new Pair(nx, ny, 0)); // 큐 삽입시점
visitedNoWall[nx][ny]=true;
}else {
if(visitedYesWall[nx][ny]) continue;
qu.offer(new Pair(nx, ny, 1)); // 큐 삽입시점
visitedYesWall[nx][ny]=true;
}
}else { // 이미 벽 부순경우
if(inArr[nx][ny] == 0) {
if(visitedYesWall[nx][ny]) continue;
qu.offer(new Pair(nx, ny, 1)); // 큐 삽입시점
visitedYesWall[nx][ny]=true;
}else {
continue; // 이미 벽도 부쉈고(wall=1), 맵에 벽을 만난경우(inArr[nx][ny]=1)
}
}
}
}
// 기록 -> 종료시점
if(visitedNoWall[N-1][M-1]) {
result1 = level; break;
}
if(visitedYesWall[N-1][M-1]) {
result2 = level; break;
}
}
//최종 출력
if(visitedNoWall[N-1][M-1] || visitedYesWall[N-1][M-1]){
System.out.println(Math.min(result1,result2));
}else{
System.out.println(-1);
}
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
N = Integer.parseInt(stk.nextToken());
M = Integer.parseInt(stk.nextToken());
for(int i=0; i<N; i++){
String inStr = br.readLine();
for(int j=0; j<M; j++){
inArr[i][j] = inStr.charAt(j)-'0'; // 숫자로 입력
}
}
//run
bfs();
}
static class Pair {
int x; int y; int wall;
public Pair(int x, int y, int wall) {
this.x=x; this.y=y; this.wall=wall;
}
@Override
public String toString() {
return "x:"+x+" y:"+y+" wall:"+wall;
}
}
}
느낀점
debug를 잘 찍는것도 좋은 습관이다!
댓글남기기