Problem:
パチモンクリーチャー

某国で大人気のゲーム、パチモンクリーチャーが日本でリメイクされて発売されました。 ゲームが大好きなあなたは、 このゲームを何度もプレイするうちにどうしたら最速でクリアできるのか考えるようになりました。しかし、いくら考えても最速の攻略方法がわからなかったあなたは、どれだけ早くゲームをクリアできるかを求めるプログラムを作成することにしました。

ゲームの詳細は以下の通りです。

パチモンクリーチャー(以下、パチクリ)という生物が多く存在する世界がゲームの舞台です。各パチクリは、火属性、氷属性、木属性、土属性、水属性の 5 種類の属性のいずれか１つの属性を持ちます。ゲームの主人公は、ゲーム開始時に好きな属性のパチクリ一匹を冒険のパートナーとして選びます。そのパチクリと共にゴールを目指し、ゴールにいるライバルを倒してパチクリマスターになることがゲームの目的です。

しかし、ライバルを倒すためには全属性のパチクリがいないと勝てないので、途中で全属性のパチクリを捕まえなければなりません。パチクリを捕まえるには属性がカギとなります。火属性のパチクリは氷属性のパチクリを捕まえることができ、同様に、氷属性は木属性、木属性は土属性、土属性は水属性、水属性は火属性を捕まえることができます。属性の関連は以下の図のようになります。

以下の図はゲームが行われるマップの一例を表しています。

主人公はパチクリを一匹もってスタート地点である「S」から出発し、一マスずつ移動しながらゴール地点である「G」を目指します。その途中で、最初に持っているパチクリ以外の 4 つの属性のパチクリを捕まえ、ゴール地点であるマス目に移動するとゲーム終了となります。

主人公は、今いるマス目から、辺を共有する隣のマス目に移動することができ、それを一回の移動と数えます。主人公がパチクリのいるマスに移動した場合、そのパチクリを捕まえられる属性のパチクリを持っていればそのパチクリを捕まえたことになります。そのマスにいるパチクリを捕まえられるかの可否にかかわらず、すべてのマスに何度でも移動することができます。

マップの大きさ(横方向の列数、縦方向の行数)とマップの初期状態を入力とし、初めに選んだパチクリの属性と、それ以外の 4 つの属性のパチクリを捕まえるのにかかる、スタート地点からゴール地点に至る最小移動数を出力するプログラムを作成してください。

Input

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

W H
c11c12...c1W
c21c22...c2W
:
cH1cH2...cHW

1 行目にマップの横方向の列数 W と縦方向の行数 H (2 ≤ W, H ≤ 1000) が与えられます。続く H 行にマップの i 行目の情報が与えられます。入力されるマップには各マスの状態が与えられます。 「S」は主人公のスタート地点を、 「G」はゴール地点を、「1」「2」「3」「4」「5」はそこにいるパチクリの属性を( 1:火属性、 2:氷属性、 3:木属性、 4:土属性、 5:水属性 をそれぞれ表します)、 「.(ピリオド) 」は何もないマスをそれぞれ表します。
各属性のパチクリの数はそれぞれ 0 以上 1000 以下とします。

データセットの数は140 を超えません。また、データセットの 80 % について、W, H は100 を超えません。

Output

入力データセットごとに、最初に選んだパチクリの属性と最小移動数を１行に出力します。なお、どのように初めのパチクリを選んでも、どのような経路で移動してもそれら 4 つの属性のパチクリを捕まえることができない場合は NA と出力してください。 また、最小移動数が同じになるような最初のパチクリの選び方が複数ある場合は、属性の数字が小さいものを出力してください。

Sample Input

6 6
S.1..4
3..5..
..4.1.
4....5
.2.32.
5.1..G
3 2
...
S.G
0 0

Output for the Sample Input

2 10
NA

Solution in JAVA (judge verdict: Time Limit Exceeded)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Main {

	/** 標準入力 */
	private static BufferedReader br = new BufferedReader(
			new InputStreamReader(System.in));
	private static final char FIRE = '1';
	private static final char FROZEN = '2';
	private static final char TREE = '3';
	private static final char MUD = '4';
	private static final char WATER = '5';
	private static final char START = 'S';
	private static final char GOAL = 'G';
	private static char[] pachimons = { FIRE, FROZEN, TREE, MUD, WATER };

	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws IOException {
		String line;
		int y = 0;
		boolean begin = true;
		int startx = -1;
		int starty = -1;
		int goalx = -1;
		int goaly = -1;
		List<int[]> fireMapInfo = new ArrayList<int[]>();
		List<int[]> frozenMapInfo = new ArrayList<int[]>();
		List<int[]> treeMapInfo = new ArrayList<int[]>();
		List<int[]> mudMapInfo = new ArrayList<int[]>();
		List<int[]> waterMapInfo = new ArrayList<int[]>();
		List<int[]> mapInfo[] = new ArrayList[5];
		int startGoal[] = new int[4];
		List<List<int[]>[]> mapInfoList = new ArrayList<List<int[]>[]>();
		List<int[]> startGoalList = new ArrayList<int[]>();

		while ((line = br.readLine()) != null && !line.isEmpty()) {
			if (line.contains(" ")) {
				if (begin) {
					begin = false;
				} else {
					assign(mapInfo, startGoal, startx, starty, goalx, goaly,
							fireMapInfo, frozenMapInfo, treeMapInfo,
							mudMapInfo, waterMapInfo, mapInfoList,
							startGoalList);
					fireMapInfo = new ArrayList<int[]>();
					frozenMapInfo = new ArrayList<int[]>();
					treeMapInfo = new ArrayList<int[]>();
					mudMapInfo = new ArrayList<int[]>();
					waterMapInfo = new ArrayList<int[]>();
					mapInfo = new ArrayList[5];
					startGoal = new int[4];
				}
				continue;
			}

			for (int i = 0, x = 0; i < line.length(); i++, x++) {
				int[] xy = { x, y };
				char cell = line.charAt(i);
				if (START == cell) {
					startx = x;
					starty = y;
				}
				if (GOAL == cell) {
					goalx = x;
					goaly = y;
				}
				if (FIRE == cell) {
					fireMapInfo.add(xy);
				}
				if (FROZEN == cell) {
					frozenMapInfo.add(xy);
				}
				if (TREE == cell) {
					treeMapInfo.add(xy);
				}
				if (MUD == cell) {
					mudMapInfo.add(xy);
				}
				if (WATER == cell) {
					waterMapInfo.add(xy);
				}
			}
			y++;
		}
		assign(mapInfo, startGoal, startx, starty, goalx, goaly, fireMapInfo,
				frozenMapInfo, treeMapInfo, mudMapInfo, waterMapInfo,
				mapInfoList, startGoalList);

		boolean NA = false;
		String message = null;

		for (int i = 0; i < mapInfoList.size() && i < startGoalList.size(); i++) {
			for (int j = 0; j < mapInfoList.get(i).length; j++) {
				if (mapInfoList.get(i)[j].isEmpty()) {
					message = "NA";
					NA = true;
					break;
				}
			}
			for (int j = 0; j < startGoalList.get(i).length; j++) {
				if (NA) {
					break;
				}

				if (startGoalList.get(i)[j] == -1) {
					message = "NA";
					NA = true;
					break;
				}
			}
			if (!NA) {
				int minDistance = 1000000000;
				char answerPachimon = '\u0000';
				for (char pachimon : pachimons) {
					int distance = getMinDistance(pachimon,
							mapInfoList.get(i)[0], mapInfoList.get(i)[1],
							mapInfoList.get(i)[2], mapInfoList.get(i)[3],
							mapInfoList.get(i)[4], startGoalList.get(i)[0],
							startGoalList.get(i)[1], startGoalList.get(i)[2],
							startGoalList.get(i)[3]);
					if (distance < minDistance) {
						answerPachimon = pachimon;
						minDistance = distance;
					}
				}
				message = answerPachimon + " " + minDistance;
			}
			System.out.println(message);
			NA = false;
		}
	}

	private static void assign(List<int[]>[] mapInfo, int[] startGoal,
			int startx, int starty, int goalx, int goaly,
			List<int[]> fireMapInfo, List<int[]> frozenMapInfo,
			List<int[]> treeMapInfo, List<int[]> mudMapInfo,
			List<int[]> waterMapInfo, List<List<int[]>[]> mapInfoList,
			List<int[]> startGoalList) {
		mapInfo[0] = fireMapInfo;
		mapInfo[1] = frozenMapInfo;
		mapInfo[2] = treeMapInfo;
		mapInfo[3] = mudMapInfo;
		mapInfo[4] = waterMapInfo;
		startGoal[0] = startx;
		startGoal[1] = starty;
		startGoal[2] = goalx;
		startGoal[3] = goaly;
		mapInfoList.add(mapInfo);
		startGoalList.add(startGoal);
	}

	private static int getMinDistance(char pachimon, List<int[]> fireMapInfo,
			List<int[]> frozenMapInfo, List<int[]> treeMapInfo,
			List<int[]> mudMapInfo, List<int[]> waterMapInfo, Integer startx,
			Integer starty, Integer goalx, Integer goaly) {
		int minDistance = 1000000000;
		if (FIRE == pachimon) {
			minDistance = calcu(frozenMapInfo, treeMapInfo, mudMapInfo,
					waterMapInfo, startx, starty, goalx, goaly, minDistance);
		} else if (FROZEN == pachimon) {
			minDistance = calcu(treeMapInfo, mudMapInfo, waterMapInfo,
					fireMapInfo, startx, starty, goalx, goaly, minDistance);
		} else if (TREE == pachimon) {
			minDistance = calcu(mudMapInfo, waterMapInfo, fireMapInfo,
					frozenMapInfo, startx, starty, goalx, goaly, minDistance);
		} else if (MUD == pachimon) {
			minDistance = calcu(waterMapInfo, fireMapInfo, frozenMapInfo,
					treeMapInfo, startx, starty, goalx, goaly, minDistance);
		} else if (WATER == pachimon) {
			minDistance = calcu(fireMapInfo, frozenMapInfo, treeMapInfo,
					mudMapInfo, startx, starty, goalx, goaly, minDistance);
		}
		return minDistance;
	}

	private static int calcu(List<int[]> mapInfo1, List<int[]> mapInfo2,
			List<int[]> mapInfo3, List<int[]> mapInfo4, Integer startx,
			Integer starty, Integer goalx, Integer goaly, int minDistance) {
		for (int[] map1 : mapInfo1) {
			for (int[] map2 : mapInfo2) {
				for (int[] map3 : mapInfo3) {
					for (int[] map4 : mapInfo4) {
						int distance = getTargetDistance(startx, starty,
								map1[0], map1[1])
								+ getTargetDistance(map1[0], map1[1], map2[0],
										map2[1])
								+ getTargetDistance(map2[0], map2[1], map3[0],
										map3[1])
								+ getTargetDistance(map3[0], map3[1], map4[0],
										map4[1])
								+ getTargetDistance(map4[0], map4[1], goalx,
										goaly);
						if (minDistance > distance) {
							minDistance = distance;
						}
					}
				}
			}
		}
		return minDistance;
	}

	private static int getTargetDistance(int currentx, int currenty, int i,
			int j) {
		return Math.abs(currentx - i) + Math.abs(currenty - j);
	}
}