Problem:
方向音痴のトナカイ

問題

今年も JOI 町にサンタクロースが空からやってきた．JOI 町にある全ての家には子供がいるので，このサンタクロースは JOI 町の全ての家にプレゼントを配ってまわらなければならない．しかし，今年は連れているトナカイが少々方向音痴であり，また建物の上以外に降りることができないため，全ての家にプレゼントを配るためには少々道順を工夫しなければならないようだ．

JOI 町は東西南北に区画整理されており，各区画は家，教会，空き地のいずれかである．JOI 町には 1 つだけ教会がある．次のルールに従い，サンタクロースとトナカイは教会から出発し，全ての家に 1 回ずつプレゼントを配った後，教会に戻る．

今年のトナカイは少々方向音痴であるため，東西南北いずれかの方向にまっすぐ飛ぶことしかできず，空中では方向転換できない．

まだプレゼントを配っていない家の上は自由に通過できる．まだプレゼントを配っていない家には降りることができる．家に降りた時は必ずプレゼントを配り，その後は東西南北いずれかの方向に飛び立つ．

JOI 町の家では，クリスマスの夜はサンタクロースが来るまでは暖炉をつけずに過ごしているが，サンタクロースが飛び立った後は暖炉をつける．暖炉をつけると煙突から煙が出るので，プレゼントを配り終えた家の上を通過することはできない．

教会の上は自由に通過することができる．しかし，教会では礼拝が行われているので，全てのプレゼントを配り終えるまで教会に降りることはできない．

空き地の上は自由に通過することができるが，空き地に降りることはできない．

入力として町の構造が与えられたとき，サンタクロースとトナカイがプレゼントを配る道順が何通りあるかを求めるプログラムを作成せよ．

入力

入力は複数のデータセットからなる．各データセットは以下の形式で与えられる．

各データセットは n+1 行ある．
1 行目には整数 m (1 ≤ m ≤ 10) と整数 n (1 ≤ n ≤ 10) が空白で区切られて書かれている．
2 行目から n+1 行目までの各行には，0，1，2 のいずれかが，
空白で区切られて m 個書かれており，各々が各区画の状態を表している．
北から i 番目，西から j 番目の区画を (i,j) と記述することにすると
(1 ≤ i ≤ n, 1 ≤ j ≤ m)，
第 i+1 行目の j 番目の値は，
区画 (i,j) が空き地である場合は 0 となり，
家である場合は 1 となり，
教会である場合は 2 となる．
教会の個数は 1 であり，家の個数は 1 以上 23 以下である．
ただし，採点用入力データでは配る道順が 2000000 通りをこえることはない．

m, n がともに 0 のとき入力の終了を示す.データセットの数は 5 を超えない．

出力

データセットごとに,プレゼントを配る道順が何通りあるかを表す整数を1 行に出力する．

入出力例

入力例

3 2
1 0 1
1 0 2
3 3
1 1 1
1 0 1
1 1 2
0 0

出力例

2
6

図: 2つ目の入力例 に対する 6 通り全ての道順
（数字は配る順序を表す）

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in JAVA (judge verdict: Memory Limit Exceeded)



import java.util.*;

import java.lang.*;

import java.math.*;



public class Main {

	Scanner sc = new Scanner(System.in);



	class b {

		int i;

		int j;



		b(int a, int c) {

			i = a;

			j = c;

		}



		boolean reachable(b o) {

			return this.i == o.i || this.j == o.j;

		}

	}



	int km(boolean[] a, int b) {

		int z = 0;

		for (int i = 0; i < a.length; i++) {

			z += a[i] ? 1 : 0;

			z *= 2;

		}

		return z * 100 + b;

	}



	int rec(boolean[] used, int now, int count, int[][] map) {

		if (ht.containsKey(km(used, now))) {

			return ht.get(km(used, now));

		}

		if (count == used.length - 1) {

			if (!list.get(now).reachable(list.get(0))){

				ht.put(km(used, now),0);

				return 0;

			}

			ht.put(km(used, now),1);

			return 1;

		}

		int sum = 0;

		int[] dx = { -1, 1, 0, 0 };

		int[] dy = { 0, 0, -1, 1 };

		b bnow = list.get(now);

		for (int i = 0; i < 4; i++) {

			int x = bnow.j;

			int y = bnow.i;

			for (;; x += dx[i], y += dy[i]) {

				if (y >= map.length || x >= map[0].length || x < 0 || y < 0) {

					break;

				}

				if (map[y][x] > 0) {

					// System.out.printf("%x ",1<<(count*4));

					// System.out.println("push"+i);

					int q = map[y][x];

					used[q] = true;

					map[y][x] = -1;

					sum += rec(used, q, count + 1, map);

					map[y][x] = q;

					// System.out.printf("%x ",1<<(count*4));

					// System.out.println("pop"+i);

					used[q] = false;

					break;

				}

			}

		}

		ht.put(km(used, now),sum);

		return sum;

	}



	LinkedList<b> list = new LinkedList<Main.b>();

	HashMap<Integer, Integer> ht = new HashMap<Integer, Integer>();



	void run() {

		for (;;) {

			int w = sc.nextInt();

			int h = sc.nextInt();

			if ((h | w) == 0) {

				break;

			}

			b start = null;

			list.clear();

			ht = new HashMap<Integer, Integer>();

			int[][] map = new int[h][w];

			int id = 1;

			for (int i = 0; i < h; i++) {

				for (int j = 0; j < w; j++) {

					map[i][j] = -1;

					int z = sc.nextInt();

					if (z == 1) {

						list.addLast(new b(i, j));

						map[i][j] = id;

						id++;

					}

					if (z == 2) {

						start = new b(i, j);

						list.addFirst(start);

						map[i][j] = 0;

					}

				}

			}

			int n = list.size();



			boolean[] used = new boolean[n];

			System.out.println(rec(used, 0, 0, map));

		}

	}



	public static void main(String[] args) {

		Main m = new Main();

		m.run();

	}

}