Problem:
11 パズル

太郎君は 8 パズルが大得意で休み時間などにいつも友達に並び替えてもらって遊んでいます。そんなとき、友達から「もっと複雑なパズルは解ける?」と聞かれたのですが、他のパズルはやったことはありません。どうやらその友達は自作で 11 パズルを作っていたみたいです。そのパズルは以下のような形をしています。

11 パズルは 11 枚の正方形のカードと、図 1 のような形の枠を使って行います。最初に 11 枚のカードを枠に入れます。すると 2 カ所の空きスペースができますので、この空きスペースに隣接したカードを動かすことができます。これを繰り返し、カードをきれいに整列して、図 2 の完成型にすることが 11 パズルの目的です。

太郎君はこのパズルに挑戦することにしました。ところが太郎君はこの 11 パズルをいとも簡単に解いてしまいました。そこで友達は「動かす数を一番少なくして解いてよ!」と無茶なことを言ってきました。太郎君は答えがわからないので、プログラムのできるあなたに 11 パズルを解くときの最小ステップ数を出すプログラムを作成してもらってから挑戦することにしました。このとき、2 カ所動かせるところがあるのですが、一つの数字を 1 スペース分移動させることを1ステップとして考えることとします。

11 パズルの初期状態を入力とし、11 パズルを解くときの最小ステップ数を出力するプログラムを作成してください。ただし、パズルを解くときの最小ステップ数が 20 ステップより多くかかってしまう場合は、「NA」と出力してください。パズルの状態は、一行目の情報から順に入力されるものとし、数字の 0 は空きスペースを表します。例えば、図 1 の状態を表す入力は以下のようになります。

6
2 1 3
10 5 7 0 8
9 4 11
0

Input

複数のデータセットの並びが入力として与えられます。入力の終わりは-1ひとつの行で示されます。 各データセットは以下の形式で与えられます。

p1
p2 p3 p4
p5 p6 p7 p8 p9
p10 p11 p12
p13

i 行目にパズルの i 行目の情報 pi (0 ≤ pi ≤ 11) が空白区切りで与えられます。

データセットの数は 100 を超えません。

Output

データセット毎に、最小ステップ数または NA を１行に出力します。

Sample Input

2
1 0 3
4 5 6 7 8
9 0 11
10
0
1 2 3
4 5 6 7 8
9 10 11
0
0
11 10 9
8 7 6 5 4
3 2 1
0
-1

Output for the Sample Input

2
0
NA

Solution in JAVA (judge verdict: Accepted)


import static java.util.Arrays.deepToString;

import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		new Main().run();
	}
	void tr(Object... os) {
		System.err.println(deepToString(os));
	}
	
	Scanner sc = new Scanner(System.in);
	
	char[] goal = "0123456789ab0".toCharArray();
	int[] rs = {0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4};
	int[] cs = {2, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 2};
	int[] dr = {0, 1, 0, -1};
	int[] dc = {1, 0, -1, 0};
	
	int[][] rm = {
		{-1, -1, -1,-1, -1, -1, -1},
		{-1, -1, -1, 0, -1, -1, -1},
		{-1, -1,  1, 2,  3, -1, -1},
		{-1,  4,  5, 6,  7,  8, -1},
		{-1, -1,  9, 10, 11, -1, -1},
		{-1, -1, -1, 12, -1, -1, -1},
		{-1, -1, -1, -1, -1, -1, -1},
	};
	
	public void run() {
		for (;sc.hasNext();) {
			char[] s = new char[13];
			int x = sc.nextInt();
			if (x == -1) break;
			s[0] = toc(x);
			for (int i = 1; i < 13; i++) {
				s[i] = toc(sc.nextInt());
			}
			Set<String> vis = new HashSet<String>();
			vis.add(new String(s));
			Queue<String> q1 = new ArrayDeque<String>();
			Queue<Integer> q2 = new ArrayDeque<Integer>();
			q1.add(new String(s));
			q2.add(0);
			int ans = -1;
			while (q1.size() > 0) {
				s = q1.poll().toCharArray();
				int step = q2.poll();
//				tr(vis.size(), q1.size(), step);
				if (isGoal(s)) {
					ans = step;
					break;
				}
				
				
				if (step < 20) {
					for (int i = 0; i < s.length; i++) if (s[i] == '0') {
						final int zr = rs[i];
						final int zc = cs[i];
						for (int k = 0; k < 4; k++) {
							int nr = zr + dr[k];
							int nc = zc + dc[k];
							int ni = rm[nr + 1][nc + 1];
							if (ni == -1) continue;
							swap(s, i, ni);
							String nxt = new String(s);
							if (!vis.contains(nxt)) {
								if (step + estimate(s) <= 20) {
									vis.add(nxt);
									q1.add(nxt);
									q2.add(step+1);
								}
							}
							swap(s, i, ni);
						}
					}
				}
			}
			
			if (ans == -1) System.out.println("NA"); else System.out.println(ans);
		}
	}
	int estimate(char[] s) {
		int sum = 0;
		for (int i = 0; i < s.length; i++) if (s[i] != '0') {
			int n = getNum(s[i]);
			int d = Math.abs(rs[i] - rs[n]) + Math.abs(cs[i] - cs[n]);
			sum += d;
		}
		return Math.max(0, sum - 2);
	}
	int getNum(char c) {
		if (c == 'a') return 10;
		if (c == 'b') return 11;
		return c - '0';
	}
	void swap(char[] s, int i, int j) {
		char t = s[i]; s[i] = s[j]; s[j] = t;
	}
	boolean isGoal(char[] s) {
		for (int i = 0; i < s.length; i++) if (s[i] != goal[i]) return false;
		return true;
	}
	char toc(int x) {
		return String.format("%x", x).charAt(0);
	}
}