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 C (judge verdict: Accepted)

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

typedef struct{
	long long int code;
	int steps;
} puzz;

int SW,q[10000000][14],size[2][30000];
long long int FR,TO;
puzz* hash[2][30000][100];

void swap (int* x,int* y){
	int z;
	z=*x;
	*x=*y;
	*y=z;
}

long long int convert(int a[]){
	long long int ret=0;
	int i;
	for(i=0;i<13;i++)ret=ret*13+a[i];
	return ret;
}

void hashadd(long long int code,int step,int loc){
	int ccc=code%30000;
	puzz* ppp=(puzz*)malloc(sizeof(puzz));
	ppp->code=code;
	ppp->steps=step;
	if(size[loc][ccc]>99){fprintf(stderr,"SIZE OVER!! (%d)\n",ccc);return;}
	hash[loc][ccc][size[loc][ccc]]=ppp;
	size[loc][ccc]++;
}

int hashsearch(long long int code,int loc){
	int ccc=code%30000;
	int i=size[loc][ccc]-1;
	for(;i>=0;i--){
		if(hash[loc][ccc][i]->code==code)return hash[loc][ccc][i]->steps;
	}
	return -1;
}

void mark(int x,int y){
	int k;
	long long int cd;

	swap(&q[FR][x],&q[FR][y]);
	cd=convert(q[FR]);

	if(hashsearch(cd,SW)<0){
		for(k=0;k<14;k++){q[TO][k]=q[FR][k];}
		q[TO][13]++;
		if(SW==0)hashadd(cd,q[TO][13],SW);
		TO++;
	}

	swap(&q[FR][x],&q[FR][y]);

}

void check(){
	if(q[FR][0]==0){mark(0,2);}
	if(q[FR][1]==0){mark(1,2);mark(1,5);}
	if(q[FR][2]==0){mark(2,0);mark(2,1);mark(2,3);mark(2,6);}
	if(q[FR][3]==0){mark(3,2);mark(3,7);}
	if(q[FR][4]==0){mark(4,5);}
	if(q[FR][5]==0){mark(5,1);mark(5,4);mark(5,6);mark(5,9);}
	if(q[FR][6]==0){mark(6,2);mark(6,5);mark(6,7);mark(6,10);}
	if(q[FR][7]==0){mark(7,3);mark(7,6);mark(7,8);mark(7,11);}
	if(q[FR][8]==0){mark(8,7);}
	if(q[FR][9]==0){mark(9,5);mark(9,10);}
	if(q[FR][10]==0){mark(10,6);mark(10,9);mark(10,11);mark(10,12);}
	if(q[FR][11]==0){mark(11,7);mark(11,10);}
	if(q[FR][12]==0){mark(12,10);}
}


int main(){
	int i,ans;
	long long int cd;

	for(i=1;i<12;i++)q[0][i]=i;
	q[0][0]=0;q[0][12]=0;q[0][13]=0;
	hashadd(convert(q[0]),0,0);

	FR=0;TO=1;SW=0;

	while(FR<TO && q[FR][13]<=13){
		check();
		FR++;
	}

	while(scanf("%d",&q[0][0])*q[0][0]>=0){
		for(i=1;i<13;i++)scanf("%d",&q[0][i]);
		q[0][13]=0;FR=0;TO=1;SW=1;ans=9999;
		for(i=0;i<30000;i++)size[1][i]=0;

		while(FR<TO){
			cd=hashsearch(convert(q[FR]),0);
			if(cd>=0){
				ans=(int)cd+q[FR][13];
				break;
			}
			if(q[FR][13]<6)check();
			FR++;
		}

		if(ans>20)printf("NA\n"); else printf("%d\n",ans);
	}

	return 0;
}