白駿-1600号は馬の猿になりたい


https://www.acmicpc.net/problem/1600

ブログ参照
https://simju9397.tistory.com/25

方法


最初はDFSを使用してストレージを行いましたが、メモリがオーバーフローしました.私はただ全体的な探求の方法を使いたいだけですが、間違いに近いので
BFSに変更します.
  • Pointオブジェクトに座標x、yと移動回数、馬と同じ移動回数がある
  • Dir配列、0~7は馬のように動く座標、8~11は一般的に上下左右に動く
  • 通常BFSのようにキューを生成しアクセス状況をチェックして操作を行い、その中にタイムアウトがある箇所Visit x, y, z配列の意味が重要で、「馬のようにZを移動してX、Y座標にアクセスしますか?」表示
  • 3-1. Zは0からKまでなので、サイズはK+1の三次元配列でなければなりません.
    3-2. 残りの部分が従来のBFSと同じかどうかを確認します.

    ソース

    import java.io.*;
    import java.util.*;
    
    public class Main {
    	static int K;
    	static int W, H;
    	static int[][] Board;
    	static int[][] Dir = {{-2, -1}, {-1, -2}, {1, -2}, {2, -1},
    							{2, 1}, {1, 2}, {-1, 2}, {-2, 1},
    							{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    	static boolean[][][] Visit;
    	static int Answer = Integer.MAX_VALUE;
    	public static void main(String[] args) throws IOException {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		StringTokenizer st = new StringTokenizer(br.readLine());
    		
    		K = Integer.parseInt(st.nextToken());
    		
    		st = new StringTokenizer(br.readLine());
    		W = Integer.parseInt(st.nextToken());
    		H = Integer.parseInt(st.nextToken());
    		
    		Board = new int[H][W];
    		Visit = new boolean[H][W][K+1]; // x, y 좌표로 이동하는데 k 말처럼 이동하여 방문하였는지 체크
    		for(int i = 0; i < H; i++) {
    			st = new StringTokenizer(br.readLine());
    			for(int j = 0; j < W; j++) {
    				Board[i][j] = Integer.parseInt(st.nextToken());
    			}
    		}
    		bfs();
    		if(Answer == Integer.MAX_VALUE) System.out.println("-1");
    		else System.out.println(Answer);
    	}
    	static void bfs() {
    		Queue<Point_b1600> q = new LinkedList<Point_b1600>();
    		q.offer(new Point_b1600(0, 0, 0, 0));
    		Visit[0][0][0] = true; //0, 0은 말처럼 0번 움직여서 방문한거
    		
    		while(!q.isEmpty()) {
    			Point_b1600 cur = q.poll();
    			int cx = cur.x;
    			int cy = cur.y;
    			int cMcnt = cur.moveCnt;
    			int cNcnt = cur.nightCnt;
    			
    			if(cx == H-1 && cy == W-1) {
    				Answer = cMcnt < Answer ? cMcnt : Answer; 
    			}
    			
    			int loopCnt = cNcnt < K ? 0 : 8;
    			
    			for(int i = loopCnt; i < Dir.length; i++) {
    				int nx = cx + Dir[i][0];
    				int ny = cy + Dir[i][1];
    				
    				//범위밖이면 바로 패스
    				if(nx < 0 || nx >= H || ny < 0 || ny >= W) continue;
    				
    				//범위 내인데, X면 못감.
    				if(Board[nx][ny] == 1) continue;
    				
    				boolean isNightMove = i < 8 ? true : false; //0~7이면 말처럼 이동이고, 8~11이면 일반 이동
    				
    				// 해당 좌표에 말처럼 움직이는데, 방문한적이 없으면 방문
    				if(isNightMove && !Visit[nx][ny][cNcnt+1]) {
    					//방문처리해주고, q에 offer
    					Visit[nx][ny][cNcnt+1] = true;
    					q.offer(new Point_b1600(nx, ny, cMcnt+1, cNcnt+1));
    				} 
    				// 해당 좌표에 말처럼 움직이지 않았는데, 방문한적이 없으면 방문
    				else if(!isNightMove && !Visit[nx][ny][cNcnt]) {
    					Visit[nx][ny][cNcnt] = true;
    					q.offer(new Point_b1600(nx, ny, cMcnt+1, cNcnt));
    				}
    			}
    		}		
    	}
    }
    
    class Point_b1600{
    	int x;
    	int y;
    	int moveCnt;
    	int nightCnt;
    	Point_b1600(int x, int y, int moveCnt, int nightCnt){
    		this.x = x;
    		this.y = y;
    		this.moveCnt = moveCnt;
    		this.nightCnt = nightCnt;
    	}
    }