Algorithm/Baekjoon Online Judge

[Java] BOJ1600_말이 되고픈 원숭이

'지훈' 2021. 4. 23. 16:33

www.acmicpc.net/problem/1600

 

1600번: 말이 되고픈 원숭이

첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있

www.acmicpc.net


Knight처럼 이동하거나 상, 하, 좌, 우 네 방향을 이동해서 도착점까지 이동해야 한다. 이때 Knight처럼 이동하는 것은 K번만 가능하다.

 

BFS로 탐색하는 문제에서 Knight로 이동하는 조건이 추가된 문제이다. 이런 경우 Knight이동을 1번만 한 경우, 2번 한 경우,... K번 한 경우를 다 고려해서 최소 동작 수를 구해줘야 한다. 따라서 방문 처리 배열을 index와 knight 이동 횟수를 담는 3차원 배열로 선언해주었다.

 

bfs를 돌 때 Queue에 현재위치, 이동 횟수, knight 이동 횟수를 담아주었다.

상, 하, 좌, 우 사방으로 이동할 때는 이동횟수만 증가시킨 채 Queue에 담고 Knight로 이동할 때는 knight 이동 횟수 또한 증가시켜서 Queue에 담아주었다. 이때 Knight 이동 횟수가 K번 이하인지 확인해야 한다.

 

import java.io.*;
import java.util.*;

public class Main {

	static int K, R, C;
	static int[][] arr;
	static boolean[][][] visit;
	static int[] dr = { -1, 0, 1, 0, -2, -1, 1, 2, 2, 1, -1, -2};
	static int[] dc = { 0, -1, 0, 1, -1, -2, -2, -1, 1, 2, 2, 1};
	static int res = -1;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		K = Integer.parseInt(br.readLine());
		StringTokenizer st = new StringTokenizer(br.readLine());
		C = Integer.parseInt(st.nextToken());
		R = Integer.parseInt(st.nextToken());
		arr = new int[R][C];
		visit = new boolean[R][C][K+1];
		for (int i = 0; i < R; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < C; j++) {
				arr[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		
		bfs(0, 0);
		System.out.println(res);
	}
	private static void bfs(int r, int c) {
		Queue<int[]> q = new LinkedList<int[]>();
		q.offer(new int[] {r, c, 0, 0});  //r, c, 거리, knight
		visit[r][c][0] = true;
		while(!q.isEmpty()) {
			int[] temp = q.poll();
			
			if (temp[0] == R - 1 && temp[1] == C - 1) {
				res = temp[2];
				break;
			}
			
			for (int i = 0; i < 12; i++) {
				int nr = temp[0] + dr[i];
				int nc = temp[1] + dc[i];
				
				if(nr < 0 || nc < 0 || nr >= R || nc >= C) continue;
				if(i <= 3 && arr[nr][nc] == 0 && !visit[nr][nc][temp[3]]) { //사방으로 한칸 이동하는 경우
					q.offer(new int[] {nr, nc, temp[2]+1, temp[3]});
					visit[nr][nc][temp[3]] = true;
				} else if(i >= 4 && temp[3] < K && arr[nr][nc] == 0 && !visit[nr][nc][temp[3]+1]) { //knight처럼 이동하는 경우 
					q.offer(new int[] {nr, nc, temp[2]+1, temp[3]+1});
					visit[nr][nc][temp[3]+1] = true;
				}
			}
		}
	}

}