Problem:
氷の迷路

四角のマスを縦横にならべた長方形の迷路があります。この迷路では東西南北の隣接するマスへ移動しながら、スタートのマスSを出発し、ゴールのマスGを目指します。マスの種類には、平原、山、氷の３種類があります。SとGは、平原のマスに置かれています。平原のマスには移動できますが、山のマスには移動できません。氷のマスは移動できますが、条件によって氷が割れ動けなくなります。

氷のマスは東西南北に隣接するもの全体でひとつの塊として扱われます。

氷の塊のなかで、マスの個数の半分より多くのマスに移動すると、塊全体が割れます。

例えば、図１は与えられた迷路の最短経路のステップ数が 11 であることを示しています。

図1

しかし、図２のように氷のマスを通って近道をしようとすると、大きさが６の氷の塊に対する４回目の移動後に動けなくなってしまいGには到達できません。

図2

このような迷路の情報を入力とし、SからGまでの最短経路のステップ数を求めるプログラムを作成してください。マスの種類はそれぞれ以下の文字で表されます：

文字（半角）

マスの種類

. （ピリオド）

平原

#（シャープ）

山

X

氷

与えられる迷路は必ず解けるものとします。迷路はその東西方向のマスの数 X、南北方向のマスの数 Y と X × Y 個の文字として与えられます。

入力

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

X Y
line1
line2
:
lineY

1行目に迷路の大きさを表す整数 X, Y (2 ≤ X, Y ≤ 12) が与えられます。続く Y 行に迷路の i 行目の情報 linei (長さ X の半角英文字列) が与えられます。

データセットの数は 40 を超えない。

出力

データセットごとに、最小のステップ数を１行に出力します。

入力例

5 5
.X.S.
.X#..
.XX##
.#XG.
..X..
7 3
SXX.XXG
X.#.#X.
XXX.XX#
4 4
S...
X.X.
GX..
...X
10 10
..XXXXX.XX
.X.#.#X.XX
SX.#X.X..X
#X.##.X.XX
..XXXX#.XX
##.##.##XX
....X.XX#X
.##X..#X#X
....XX#..X
...#XXG..X
0 0

出力例

11
10
10
33

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _0247
{
    class Program
    {
        enum Field { Plain = -3, Mountain = -2, Ice = -1 }
        static readonly int[] di = new int[] { 0, 1, 0, -1 };
        static readonly int[] dj = new int[] { 1, 0, -1, 0 };

        static int h, w;
        static int sI, sJ, gI, gJ;
        static int[,] map;
        static bool[,] visited;
        static int[] passedIce;
        static int[] iceCnts;

        static void Main(string[] args)
        {
            while (true)
            {
                int[] xy = RArInt();
                if (xy.Sum() == 0) break;
                Init(xy);
                for (int u = 0; u < h * w; u++)
                {
                    if (CalcMinCost(u, sI, sJ, 0))
                    {
                        Console.WriteLine(u);
                        break;
                    }
                }
            }
        }

        private static void Init(int[] xy)
        {
            w = xy[0]; h = xy[1];
            map = new int[h, w];
            visited = new bool[h, w];
            for (int i = 0; i < h; i++)
            {
                string ms = RSt();
                for (int j = 0; j < w; j++)
                {
                    switch (ms[j])
                    {
                        case '#':
                            map[i, j] = (int)Field.Mountain;
                            break;
                        case 'X':
                            map[i, j] = (int)Field.Ice;
                            break;
                        default:
                            map[i, j] = (int)Field.Plain;
                            if (ms[j] == 'S') { sI = i; sJ = j; }
                            else if (ms[j] == 'G') { gI = i; gJ = j; }
                            break;
                    }
                }
            }
            SetIce();
            passedIce = new int[iceCnts.Length];
        }

        private static void SetIce()
        {
            iceCnts = new int[100];
            int num = 0;
            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    if (map[i, j] == (int)Field.Ice)
                    {
                        SetIceDFS(i, j, num);
                        num++;
                    }
                }
            }
        }

        private static void SetIceDFS(int i, int j, int num)
        {
            map[i, j] = num;
            iceCnts[num]++;
            for (int k = 0; k < di.Length; k++)
            {
                int nI = i + di[k];
                int nJ = j + dj[k];
                if (!(0 <= nI && nI < h && 0 <= nJ && nJ < w) || map[nI, nJ] != (int)Field.Ice) continue;
                SetIceDFS(nI, nJ, num);
            }
        }

        private static bool CalcMinCost(int u, int i, int j, int cost)
        {
            if (i == gI && j == gJ)
            {
                return true;
            }

            visited[i, j] = true;
            int eValue = cost + Math.Abs(i - gI) + Math.Abs(j - gJ);
            if (eValue <= u)
            {
                for (int k = 0; k < di.Length; k++)
                {
                    int nI = i + di[k];
                    int nJ = j + dj[k];
                    if (!(0 <= nI && nI < h && 0 <= nJ && nJ < w) || map[nI, nJ] == (int)Field.Mountain || visited[nI, nJ]) continue;

                    if (map[nI, nJ] != (int)Field.Plain)
                    {
                        int iceIdx = map[nI, nJ];
                        if ((passedIce[iceIdx] + 1) * 2 <= iceCnts[iceIdx])
                        {
                            passedIce[iceIdx]++;
                            visited[nI, nJ] = true;
                            if (CalcMinCost(u, nI, nJ, cost + 1)) return true;
                            visited[nI, nJ] = false;
                            passedIce[iceIdx]--;
                        }
                    }
                    else
                    {
                        visited[nI, nJ] = true;
                        if (CalcMinCost(u, nI, nJ, cost + 1)) return true;
                        visited[nI, nJ] = false;
                    }
                }
            }
            visited[i, j] = false;
            return false;
        }

        static string RSt() { return Console.ReadLine(); }
        static int RInt() { return int.Parse(Console.ReadLine().Trim()); }
        static long RLong() { return long.Parse(Console.ReadLine().Trim()); }
        static double RDouble() { return double.Parse(Console.ReadLine()); }
        static string[] RArSt(char sep = ' ') { return Console.ReadLine().Trim().Split(sep); }
        static int[] RArInt(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
        static long[] RArLong(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => long.Parse(e)); }
        static double[] RArDouble(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => double.Parse(e)); }
        static string WAr<T>(IEnumerable<T> array, string sep = " ") { return string.Join(sep, array.Select(x => x.ToString()).ToArray()); }
    }
}