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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern;

public class Main {
    private static Pattern PATTERN_FIELD_START = Pattern.compile("\\d \\d");

    public static void main(String[] args) {
        // 読み込んでフィールドを作る
        Scanner sc = new Scanner(System.in);

        List<String> fieldInput = new ArrayList<String>();
        while (sc.hasNext()) {
            String input = sc.nextLine();
            if (PATTERN_FIELD_START.matcher(input).matches()) {
                // 「n n」形式だったらフィールド開始なので、バッファリングした文字を基にフィールドを作る
                if (fieldInput.isEmpty()) {
                    continue;
                }
                PachimonField field = craeteField(fieldInput);
                searchMinimal(field);

                fieldInput.clear();
            } else {
                // それ以外の形式であればフィールド情報なのでバッファリング
                fieldInput.add(input);
            }
        }

    }

    /** フィールドを作る */
    private static PachimonField craeteField(List<String> fieldInput) {
        PachimonField field = new PachimonField();

        for (int y = 0; y < fieldInput.size(); y++) {
            String xAxis = fieldInput.get(y);
            for (int x = 0; x < xAxis.length(); x++) {
                char c = xAxis.charAt(x);

                switch (c) {
                // だっさい処理だなぁ…
                case 'S':
                    field.start = new Position(x, y);
                    break;
                case 'G':
                    field.end = new Position(x, y);
                    break;
                case '1':
                    field.elements.get(1).add(new Position(x, y));
                    break;
                case '2':
                    field.elements.get(2).add(new Position(x, y));
                    break;
                case '3':
                    field.elements.get(3).add(new Position(x, y));
                    break;
                case '4':
                    field.elements.get(4).add(new Position(x, y));
                    break;
                case '5':
                    field.elements.get(5).add(new Position(x, y));
                    break;
                default:
                    // not any
                }
            }
        }

        return field;
    }

    /** 属性を指定せず最小距離を出す */
    private static void searchMinimal(PachimonField field) {
        int minDistance = Integer.MAX_VALUE;
        int element = -1;

        for (int i = 1; i <= 5; i++) {
            int distance = searchMinimalByElement(i, field);
            if (distance != -1 && distance < minDistance) {
                minDistance = distance;
                element = i;
            }
        }

        if (element == -1) {
            System.out.println("NA");
        } else {
            System.out.println(Integer.toString(element) + " "
                    + Integer.toString(minDistance));
        }

    }

    /** 属性を指定して最小距離を出す */
    private static int searchMinimalByElement(int startElement,
            PachimonField field) {

        if (field.elements.get(getNextElementNum(startElement)).size() == 0
                || field.elements.get(getNextElementNum(startElement + 1))
                        .size() == 0
                || field.elements.get(getNextElementNum(startElement + 2))
                        .size() == 0
                || field.elements.get(getNextElementNum(startElement + 3))
                        .size() == 0) {
            // 手持ち以外の属性が居なければクリア不可能
            return -1;
        }

        int result = Integer.MAX_VALUE;
        for (Position p1 : field.elements.get(getNextElementNum(startElement))) {
            for (Position p2 : field.elements
                    .get(getNextElementNum(startElement + 1))) {
                for (Position p3 : field.elements
                        .get(getNextElementNum(startElement + 2))) {
                    for (Position p4 : field.elements
                            .get(getNextElementNum(startElement + 3))) {
                        int totalDistance = dist(field.start, p1)
                                + dist(p1, p2) + dist(p2, p3) + dist(p3, p4)
                                + dist(p4, field.end);
                        if (totalDistance < result) {
                            result = totalDistance;
                        }
                    }
                }
            }
        }

        return result;
    }

    private static int getNextElementNum(int elementNum) {
        int result = (elementNum % 5) + 1;
        return result;
    }

    /** 移動距離 */
    private static int dist(Position from, Position to) {
        return(Math.abs(from.x - to.x) + Math.abs(from.y - to.y));
    }

    /** 場所を表すオブジェクト */
    private static class Position {
        public int x;

        public int y;

        public Position(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    /** フィールド情報を表すオブジェクト */
    private static class PachimonField {
        public Position start;

        public Position end;

        public Map<Integer, List<Position>> elements = new HashMap<Integer, List<Position>>();

        public PachimonField() {
            this.elements.put(1, new ArrayList<Position>());
            this.elements.put(2, new ArrayList<Position>());
            this.elements.put(3, new ArrayList<Position>());
            this.elements.put(4, new ArrayList<Position>());
            this.elements.put(5, new ArrayList<Position>());
        }
    }

}