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)

// AOJ 0247: Ice Maze
// 2018.3.20 bal4u

#include <stdio.h>
#include <string.h>

typedef unsigned char uchar;

// [library]

/* priority queue library for C */
#define QMAX 1000
typedef struct { char r, c; uchar t; } QUE;
QUE que[QMAX]; int qsize;

#define PARENT(i) ((i)>>1)
#define LEFT(i)   ((i)<<1)
#define RIGHT(i)  (((i)<<1)+1)

void min_heapify(int i)
{
	int l, r, min;
	QUE qt;

	l = LEFT(i), r = RIGHT(i);
	if (l < qsize && que[l].t < que[i].t) min = l; else min = i;
	if (r < qsize && que[r].t < que[min].t) min = r;
	if (min != i) {
		qt = que[i], que[i] = que[min], que[min] = qt;
		min_heapify(min);
	}
}

void deq()
{
	que[0] = que[--qsize];
	min_heapify(0);
}

void enq(int r, int c, int t)
{
	int i, min;
	QUE qt;

	i = qsize++;
	que[i].r = r, que[i].c = c, que[i].t = t;
	while (i > 0 && que[min = PARENT(i)].t > que[i].t) {
		qt = que[i], que[i] = que[min], que[min] = qt;
		i = min;
	}
}

/* UNION-FIND */
int id[150], size[150];
void init(int n) { int i; for (i = 0; i < n; i++) id[i] = i, size[i] = 1; }
int root(int i) { while (i != id[i]) id[i] = id[id[i]], i = id[i]; return i; }
int connected(int p, int q) { return root(p) == root(q); }
void unite(int p, int q)
{
    int i = root(p), j = root(q); if (i == j) return;
    if (size[i] < size[j]) id[i] = j, size[j] += size[i]; else id[j] = i, size[i] += size[j];
}

/* hash */
#define HASHSIZ 50021
typedef struct { char r, c; int id; uchar t, ice[50]; } HASH;
HASH hash[HASHSIZ+2], *hashend = hash+HASHSIZ;
int insert(char r, char c, uchar t, uchar *ice, int n)
{
	HASH *hp;
	uchar *p = ice;	int k = n;
	unsigned long long id = 0;
	while (n--) id = (id << 3) + (*p++);
	id = 1 + (id % HASHSIZ), hp = hash + id;
	while (hp->id) {
		if (hp->r == r && hp->c == c && hp->t == t && !memcmp(ice, hp->ice, k)) return 1;
		if (++hp == hashend) hp = hash;
	}
	hp->r = r, hp->c = c, hp->id = (int)id, hp->t = t, memcpy(hp->ice, ice, k);
	return 0;
}
// [\library]

#define MAX 1000
typedef struct { char r, c, pr, pc; uchar t, ice[50]; } Q;
Q     q[MAX+2]; int top;
char  map[12][13];
uchar dist[12][12];
int   mv[4][2] = {{0,-1},{1,0},{0,1},{-1,0}};
uchar memo[12][12], blkid[12][12], maxice[12][12], blk[144]; int sz;

void dijkstra(int w, int h, int sr, int sc)
{
	int i, r, c, nr, nc, t;

	memset(dist, 0xa0, sizeof(dist));
	qsize = 0;
	dist[sr][sc] = 0, enq(sr, sc, 0);
	while (qsize) {
		r = que[0].r, c = que[0].c, t = que[0].t, deq();
		if (dist[r][c] < t) continue;
		for (i = 0; i < 4; i++) {
			nr = r + mv[i][0], nc = c + mv[i][1];
			if (nr < 0 || nr >= h || nc < 0 || nc >= w || map[nr][nc] == '#') continue;
			if (dist[nr][nc] > t+1) dist[nr][nc] = t+1, enq(nr, nc, t+1);
		}
	}
}

int main()
{
    int w, h, i, r, c, f, ans;
	int sr, sc, gr, gc, nr, nc;
    Q   s, ns;

    while (scanf("%d%d", &w, &h) && w > 0) {
		init(w*h);
		i = 0; for (r = 0; r < h; r++) {
			scanf("%s", map[r]);
			for (c = 0; c < w; c++) {
				memo[r][c] = i++;
				if      (map[r][c] == 'S') map[r][c] = '.', sr = r, sc = c;
				else if (map[r][c] == 'G') map[r][c] = '.', gr = r, gc = c;
			}
		}

		for (r = 0; r < h; r++) for (c = 0; c < w; c++) {
			if (map[r][c] != 'X') continue;
			for (i = 0; i < 4; i++) {
				nr = r + mv[i][0], nc = c + mv[i][1];
				if (nr < 0 || nr >= h || nc < 0 || nc >= w ||
					map[nr][nc] != 'X') continue;
				unite(memo[r][c], memo[nr][nc]);
			}
		}
		memset(blkid, 0, sizeof(blk)), memset(maxice, 0, sizeof(maxice));
		memset(blk, 0xff, sizeof(blk));
		f = 0, sz = 0;
		for (r = 0; r < h; r++) for (c = 0; c < w; c++) {
			if (map[r][c] != 'X') continue;
			i = root(memo[r][c]);
			if (size[i] == 1) { map[r][c] = '#'; continue; }
			if (blk[i] == 0xff) blk[i] = sz++;
			blkid[r][c] = blk[i];
			maxice[r][c] = size[i] >> 1, f = 1;
		}

        dijkstra(w, h, gr, gc);
		if (!f) { printf("%d\n", dist[sr][sc]); continue; }

		for (ans = dist[sr][sc]; ans < 144; ans++) {
			memset(hash, 0, sizeof(hash));
			memset(&s, 0, sizeof(Q));
			s.r = s.pr = sr, s.c = s.pc = sc, s.t = 0;
			q[0] = s, top = 1;

			while (top) {
				s = q[--top];

				if (s.r == gr && s.c == gc) goto done;
				if (s.t + dist[s.r][s.c] > ans) continue;

				if (insert(s.r, s.c, s.t, s.ice, sz)) continue;

				for (i = 0; i < 4; i++) {
					nr = s.r + mv[i][0], nc = s.c + mv[i][1];
					if (nr == s.pr && nc == s.pc) continue;
					if (nr < 0 || nr >= h || nc < 0 || nc >= w
						|| map[nr][nc] == '#') continue;
					if (map[nr][nc] == 'X' && s.ice[blkid[nr][nc]] >= maxice[nr][nc]) continue;
					ns = s, ns.r = nr, ns.pr = r, ns.c = nc, ns.pc = c, ns.t = s.t+1;
					if (map[nr][nc] == 'X') ns.ice[blkid[nr][nc]]++;
					q[top++] = ns;
				}
			}
		}
done:	printf("%d\n", ans);
	}
	return 0;
}