AOJ2137 Time Trial

問題リンク Time Trial

  • 概要

W*Hのマップがある。
マップには岩とパネルが3つずつあり、全ての岩をパネルの上に運びたい。
人は1ステップにつき、上下左右に動ける。進んだ先に岩があった場合、その先が壁や別の岩でなければ押すことができる。
このパズルを攻略する最短ステップ数を答えよ。
以下のことが保証されている。
パズルは必ず解ける
壁でないマスの数は高々50
岩とパネルは必ず3つずつある
マップの周囲は必ず壁である
4 <= W, H <= 16

  • 解法

マスが高々50しかないので、岩の状態は 50C3 = 19600 通りしかありません。これに加えて人物の状態も50通りまでしかないので、結局、パズルの状態は19600 * 50 = 980000 < 10^6 通りしかないことになります。よって幅優先探索で解くことができます。
パズルの状態をうまくコーディングできるかがカギになると思います。

  • ソース
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

//Time Trial
public class AOJ2137 {

	void run(){
		Scanner sc = new Scanner(System.in);
		int[][] d = {{-1,0},{0,1},{1,0},{0,-1}};
		//岩の状態(i, j, k)に0〜19599の番号を振る ※(i < j < k)
		int[][][] assign = new int[50][50][50];
		//岩の状態の番号から、岩の位置を復元する表
		int[][] ref = new int[19600][3];
		int ID = 0;
		for(int i=0;i<50;i++)for(int j=i+1;j<50;j++)for(int k=j+1;k<50;k++){
			ref[ID][0] = i; ref[ID][1] = j; ref[ID][2] = k;
			assign[i][j][k]=ID++;
		}
		boolean[][] u = new boolean[19600][50];
		for(;;){
			int w = sc.nextInt(), h = sc.nextInt();
			if((w|h)==0)break;
			char[][] map = new char[h][];
			//id[i][j]: (i, j)マスにつけた番号
			//番号 A のマスの座標 = (rid[A][0], rid[A][1])
			int[][] id = new int[h][w], rid = new int[50][2];
			int[] tile = new int[3], rock = new int[3];
			int kt = 0, kr = 0, start = 0, M = 0;
			for(int i=0;i<h;i++){
				map[i] = sc.next().toCharArray();
				for(int j=0;j<w;j++){
					if(map[i][j]=='@'){
						id[i][j] = M;
						rid[M][0] = i; rid[M][1] = j;
						start = M++;
					}
					else if(map[i][j]=='_'){
						id[i][j] = M;
						rid[M][0] = i; rid[M][1] = j;
						tile[kt++] = M++;
					}
					else if(map[i][j]=='*'){
						id[i][j] = M;
						rid[M][0] = i; rid[M][1] = j;
						rock[kr++] = M++;
					}
					else if(map[i][j]=='.'){
						rid[M][0] = i; rid[M][1] = j;
						id[i][j] = M++;
					}
					else id[i][j] = -1;
				}
			}
			int G = assign[tile[0]][tile[1]][tile[2]], S = assign[rock[0]][rock[1]][rock[2]];
			for(boolean[]a:u)Arrays.fill(a, false);
			u[S][start] = true;
			List<int[]> l = new ArrayList<int[]>();
			l.add(new int[]{S, start});
			int step = 0, res = -1;
			while(!l.isEmpty()){
				List<int[]> next = new ArrayList<int[]>();
				for(int[] a:l){
					if(G==a[0]){
						res = step; next.clear(); break;
					}
					int[] r = ref[a[0]], nr = new int[3];
					int pi = rid[a[1]][0], pj = rid[a[1]][1];
					for(int k=0;k<4;k++){
						int ni = pi+d[k][0], nj = pj+d[k][1], npos = id[ni][nj];
						if(npos==-1)continue;
						nr[0] = r[0]; nr[1] = r[1]; nr[2] = r[2];
						int mpos = id[ni+d[k][0]][nj+d[k][1]];
						if(r[0]==npos){
							if(mpos==-1||r[1]==mpos||r[2]==mpos)continue;
							nr[0] = mpos;
						}
						else if(r[1]==npos){
							if(mpos==-1||r[0]==mpos||r[2]==mpos)continue;
							nr[1] = mpos;
						}
						else if(r[2]==npos){
							if(mpos==-1||r[0]==mpos||r[1]==mpos)continue;
							nr[2] = mpos;
						}
						Arrays.sort(nr);
						int ns = assign[nr[0]][nr[1]][nr[2]];
						if(!u[ns][npos]){
							u[ns][npos] = true; next.add(new int[]{ns, npos});
						}
					}
				}
				step++;
				l = next;
			}
			System.out.println(res);
		}
	}

	public static void main(String[] args) {
		new AOJ2137().run();
	}
}