Problem:
ザ・スクエアーズ

この度、有名なテーマパークに、巨大迷路ザ・スクエアーズが新しく完成しました。 消防署の指導により避難訓練をしなければなりませんが、巨大迷路なだけに訓練にかかる時間を予測することができません。そこで、あなたは以下の仕様をもとに避難訓練シミュレータを開発することになりました。

巨大迷路は図 1 に示すように、横 W 、縦 H の W × H 個のマス目で表わされます。各マス目は、通路(白いマス目)、壁(茶色いマス目) 、非常口(緑のマス目)のいずれかです。図中の○は人を表し、その中の英小文字(E、W、S、N)はその人が向いている方角(東西南北)を表しています。図は上方向が北になるように描かれています。

図1

巨大迷路内にいる人は最初、東西南北のいずれかの方向を向いて立っています。各人は 1 秒単位で同時に次に示す手順で移動を試みます。

現在向いている方向の、右、前、左、後のマス目を順番に調べ、最初に見つけた、空いている通路または非常口の方向に向きを変えます。そのようなマス目が無い場合は向きを変えません。

目の前のマス目が空いていて、他の人の目の前のマス目になっていない場合は移動します。同じマス目を目の前のマスとする人が複数いる場合は、そのマス目の、東、北、西、南のマス目にいる人の順で選択された 1 人が移動します。

移動後に非常口に到着した人は、無事避難し迷路内から消えます。

与えられた巨大迷路と人の位置情報を入力とし、全ての人が避難し終える時間を出力するプログラムを作成してください。 脱出に 180 秒より長い時間を要する場合は NA と出力して下さい。 迷路と人の位置情報は、 H 行 W 列の文字によって与えられます。各文字の意味は以下のとおりです。

# : 壁

. : 床

X : 非常口

E : 東を向いている人

N : 北を向いている人

W : 西を向いている人

S : 南を向いている人

なお、迷路と外部との境界は壁 # または非常口 X のいずれかです。また、巨大迷路の中には、人が必ず１人以上います。

Input

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

W H
str1
str2
:
strH

1 行目に迷路の横方向の大きさ W、縦方向の大きさ H (1 ≤ W, H ≤ 30) が与えられます。続く H 行に迷路の i 行目を表す文字列 stri (長さ W) が与えられます。

データセットの数は 50 を超えません。

Output

入力データセットごとに、全ての人が避難し終える時間を１行に出力します。

Sample Input

10 3
##########
#E.......X
##########
4 4
####
#N.#
#..X
####
5 5
#####
#N..#
###.X
#S..#
#####
6 6
######
#..#X#
#.EE.#
####N#
#....#
######
8 8
##X#####
#....E.#
#####.##
#.#...##
#.W.#..#
#.#.N#.X
#X##.#.#
########
0 0

Output for the Sample Input

8
NA
9
16
10

Solution in JAVA (judge verdict: Accepted)

import java.awt.Point;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;

public class Main {
    Scanner sc = new Scanner(System.in);

    void run() {
        Map<Integer, Character> dir = new HashMap<Integer, Character>();
        dir.put(0, 'W');
        dir.put(2, 'E');
        dir.put(1, 'S');
        dir.put(3, 'N');
        while (true) {
            // 1 行目 迷路の大きさ横 W 縦 H (整数 整数 ; 半角空白区切り)
            // 2 行目 迷路の 1 行目の情報 (半角英文字列)
            // :
            // H+1 行目 迷路の H 行目の情報
            int w = sc.nextInt();
            int h = sc.nextInt();
            if ((h | w) == 0)
                break;
            char[][] map = new char[h][w];
            for (int i = 0; i < h; i++) {
                map[i] = sc.next().toCharArray();
            }
            Queue<Man> q = new LinkedList<Man>();
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    if (map[i][j] == 'X' || map[i][j] == '.'
                            || map[i][j] == '#')
                        continue;
                    switch (map[i][j]) {
                    case 'W':
                        q.add(new Man(j, i, 0));
                        break;
                    case 'N':
                        q.add(new Man(j, i, 3));
                        break;
                    case 'E':
                        q.add(new Man(j, i, 2));
                        break;
                    case 'S':
                        q.add(new Man(j, i, 1));
                        break;
                    }
                }
            }
            for (int t = 1; t <= 180; t++) {
                Queue<Man> pq = new PriorityQueue<Man>();
                while (!q.isEmpty()) {
                    Man m = q.poll();
                    int x = m.x;
                    int y = m.y;
                    int d = m.d;
                    int dx[] = { 0, -1, 0, 1 };
                    int dy[] = { -1, 0, 1, 0 };
                    boolean imp = true;
                    for (int i = 0; i < 4; i++) {
                        int nx = x + dx[(i + d) % 4];
                        int ny = y + dy[(i + d) % 4];
                        if (nx >= 0 && ny >= 0 && nx < w && ny < h
                                && (map[ny][nx] == '.' || map[ny][nx] == 'X')) {
                            pq.add(new Man(x, y, (d + i + 3) % 4));
                            imp = false;
                            break;
                        }
                    }
                    if (imp) {
                        pq.add(new Man(x, y, d));
                    }
                }
                char[][] nmap = new char[h][w];
                for (int i = 0; i < h; i++) {
                    for (int j = 0; j < w; j++) {
                        nmap[i][j] = map[i][j];
                    }
                }
                int dx[] = { -1, 0, 1, 0 };
                int dy[] = { 0, 1, 0, -1 };
                Set<Point> set = new HashSet<Point>();
                while (!pq.isEmpty()) {
                    Man m = pq.poll();
                    int x = m.x;
                    int y = m.y;
                    int d = m.d;
                    int nx = x + dx[d];
                    int ny = y + dy[d];
                    if (nx < 0 || ny < 0 || nx >= w || ny >= h) {
                        q.add(m);
                        continue;
                    }
                    if (map[ny][nx] == '.') {
                        if (nmap[ny][nx] == '.') {
                            nmap[ny][nx] = dir.get(d);
                            nmap[y][x] = '.';
                            q.add(new Man(nx, ny, d));
                        } else {
                            q.add(m);
                        }
                    } else if (map[ny][nx] == 'X') {
                        Point p = new Point(nx, ny);
                        if (set.contains(p)) {
                            q.add(m);
                            continue;
                        }
                        set.add(p);
                        nmap[y][x] = '.';
                    } else {
                        q.add(m);
                    }
                }
                if (q.isEmpty()) {
                    System.out.println(t);
                    break;
                }
                if (t == 180) {
                    System.out.println("NA");
                }
                map = nmap;
            }
        }
    }

    public static void main(String[] args) {
        new Main().run();
    }
}

class Man implements Comparable<Man> {
    int x;
    int y;
    int d; // E:2 N:3 W:0 S:1

    Man(int x, int y, int d) {
        this.x = x;
        this.y = y;
        this.d = d;
    }

    public String toString() {
        return "<x,y> = " + "<" + x + "," + y + "> " + "d = " + d;
    }

    @Override
    public int compareTo(Man o) {
        // TODO 自動生成されたメソッド・スタブ
        return d - o.d;
    }

}