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: Accepted)

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

	public static final int BIG_NUM = 2000000000;
	public static final int MOD = 1000000007;
	public static final long HUGE_NUM = 99999999999999999L;
	public static final double EPS = 0.000000001;

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {

		//BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		Scanner scanner = new Scanner(System.in);

		int SIZE = 1005;

		Info info[] = new Info[5*SIZE];
		for(int i = 0; i < 5*SIZE; i++){

			info[i] = new Info();
		}

		char base_map[][] = new char[SIZE][SIZE];

		int min_dist[] = new int[5*SIZE];
		int table[] = {1,2,3,4,5};

		List<Integer> list[] = new ArrayList[6];
        for(int i = 1; i <= 5; i++){

            list[i] = new ArrayList<Integer>();
        }

        int start_row = 0,start_col = 0,goal_row = 0,goal_col = 0;
        int num_creature;

        StringBuilder ans = new StringBuilder();

        while(true){

        	int W = scanner.nextInt();
        	int H = scanner.nextInt();

        	if(W == 0 && H == 0)break;

        	for(int i = 1; i <= 5; i++){

        		list[i].clear();
        	}

        	num_creature = 0;

        	for(int row = 0; row < H; row++){

        		char work[] = scanner.next().toCharArray();
        		for(int col = 0; col < W; col++){

        			base_map[row][col] = work[col];
        			if(base_map[row][col] == '.')continue;

        			if(base_map[row][col] >= '1' && base_map[row][col] <= '5'){

        				info[num_creature].set(row, col);
        				list[base_map[row][col]-'0'].add(num_creature++);

        			}else if(base_map[row][col] == 'S'){

        				start_row = row;
        				start_col = col;

        			}else{

        				goal_row = row;
        				goal_col = col;
        			}
        		}
        	}

        	int ans_start = -1;
    		int minimum = BIG_NUM;

        	for(int start = 0; start <= 4; start++){ //tableにおける、最初に一緒にいるパチクリの番号

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

        			min_dist[i] = BIG_NUM;
        		}

        		int from = table[start],to = table[(start+1)%5];

        		//スタートから最初のパチクリへ
        		for(int i = 0; i < list[to].size(); i++){

        			int next = list[to].get(i);
        			min_dist[next] = Math.abs(start_row-info[next].row)+Math.abs(start_col-info[next].col);
        		}

        		for(int add = 2; add <= 4; add++){
        			from = to;
        			to = table[(start+add)%5];

        			for(int i = 0; i < list[from].size(); i++){
        				int from_node = list[from].get(i);
        				for(int k = 0; k < list[to].size(); k++){
        					int to_node = list[to].get(k);

        					min_dist[to_node] = Math.min(min_dist[to_node], min_dist[from_node]
        							+Math.abs(info[from_node].row-info[to_node].row)+Math.abs(info[from_node].col-info[to_node].col));
        				}
        			}
        		}

        		//最後のパチクリからゴールへ
        		from = to;
        		for(int i = 0; i < list[from].size(); i++){
        			int from_node = list[from].get(i);

        			if(minimum > min_dist[from_node]
        					+Math.abs(info[from_node].row-goal_row)+Math.abs(info[from_node].col-goal_col)){
        				minimum = min_dist[from_node]
            					+Math.abs(info[from_node].row-goal_row)+Math.abs(info[from_node].col-goal_col);
        				ans_start = table[start];
        			}
        		}
        	}

			if(minimum == BIG_NUM){

				ans.append("NA\n");
			}else{

				ans.append(Integer.toString(ans_start)).append(" ").append(Integer.toString(minimum)).append("\n");
			}
        }
        System.out.print(ans.toString());
	}
}

class Info{

	public int row;
	public int col;

	public void set(int arg_row,int arg_col){
		row = arg_row;
		col = arg_col;
	}
}

class UTIL{

	//最大公約数
	public static long gcd(long x,long y){

		x = Math.abs(x);
		y = Math.abs(y);

		if(x < y){

			long tmp = y;
			y = x;
			x = tmp;
		}

		if(y == 0){

			return x;
		}else{

			return gcd(y,x%y);
		}
	}

	//最小公倍数
	public static long lcm(long x,long y){

		return (x*y)/gcd(x,y);
	}

	//String→intへ変換
    public static int getNUM(String tmp_str){

        return Integer.parseInt(tmp_str);
    }

    //文字が半角数字か判定する関数
    public static boolean isNumber(String tmp_str){

        if(tmp_str == null || tmp_str.length() == 0){

            return false;
        }

        Pattern pattern = Pattern.compile("\\A[0-9]+\\z");
        Matcher matcher = pattern.matcher(tmp_str);
        return matcher.matches();
    }
}