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 JAVA (judge verdict: Accepted)

import java.util.*;

public class Main {
	Scanner sc = new Scanner(System.in);
	int [] vx = {0,1,0,-1};
	int [] vy = {1,0,-1,0};
	int w,h, gx, gy;
	char [][] data;
	
	class State implements Comparable<State>{
		int x, y, step, restdist;
		int [] enter;
		public State(int x, int y, int step, int [] enter, int restdist) {
			this.x = x;
			this.y = y;
			this.step = step;
			this.enter = enter;
			this.restdist = restdist;
		}
		@Override
		public int compareTo(State o) {
			return (this.step + this.restdist) - (o.step + o.restdist);
		}
	}
	
	private void doit(){
		while(true){
			w = sc.nextInt();
			h = sc.nextInt();
			if((w|h) == 0) break;
			data = new char[h][w];
			for(int i = 0 ; i < h; i++){
				data[i]= sc.next().toCharArray();
			}
			int sx = -1, sy = -1;
			ArrayList<Integer> list = new ArrayList<Integer>();
			char c = '0';
			for(int i = 0; i < h; i++){
				for(int j = 0; j < w; j++){
					if(data[i][j] == 'S'){
						data[i][j] = '.';
						sx = j;
						sy = i;
					}
					else if(data[i][j] == 'G'){
						gx = j;
						gy = i;
					}
					else if(data[i][j]== 'X'){
						int res = dfs(i,j, c);
						if(res == 1){
							data[i][j] = '#';
						}
						else{
							list.add(res / 2);
							c++;
							if(c == 'G' || c == 'S'){
								c++;
							}
						}
					}
				}
			}
			
			int [] initenter = new int[list.size()];
			for(int i = 0; i < initenter.length; i++){
				initenter[i] = list.get(i);
			}
			
			PriorityQueue<State> open = new PriorityQueue<Main.State>();
			open.add(new State(sx, sy, 0, initenter, calcM(sx,sy)));
			HashSet<String> close = new HashSet<String>();
			close.add((sy * w + sx)+ " "+ getState(initenter));
			int ans = -1;
			while(! open.isEmpty()){
				State now = open.poll();
				boolean flg = false;
				for(int i = 0; i < 4; i++){
					int xx = now.x + vx[i];
					int yy = now.y + vy[i];
					if(! isOK(xx, yy)) continue;
					if(data[yy][xx] == '#') continue;
					
					
					int [] nextenter = (int[]) now.enter.clone();
					if(data[yy][xx]== 'G' ){
						ans = now.step + 1;
						flg =true;
						break;
					}
					else if(data[yy][xx] != '.'){
						int ind = data[yy][xx] - '0';
						if(ind >= ('U' - '0')){
							ind--;
						}
						if(ind >= ('G' - '0')){
							ind--;
						}
						if(nextenter[ind] <= 0) continue;
						nextenter[ind]--;
					}
					String nextstate = (yy * w + xx) + " " + getState(nextenter);
					if(close.contains(nextstate))continue;
					open.add(new State(xx, yy, now.step + 1, nextenter, calcM(xx, yy)));
					close.add(nextstate);
				}
				if(flg){
					break;
				}
			}
			System.out.println(ans);
		}
	}

	private int calcM(int sx, int sy) {
		int res = Math.abs(sx - gx) + Math.abs(sy - sy);
		return res;
	}

	private String getState(int [] list) {
		StringBuilder sb = new StringBuilder();
		for(int value: list){
			sb.append(" " + value);
		}
		return sb.length() == 0 ? "" : sb.substring(1);
	}

	private int dfs(int y, int x, char c) {
		int sum = 1;
		data[y][x] = c;
		for(int i = 0; i < 4; i++){
			int xx = x + vx[i];
			int yy = y + vy[i];
			if(! isOK(xx, yy)) continue;
			if(data[yy][xx] == 'X'){
				sum += dfs(yy,xx,c);
			}
		}
		return sum;
	}

	private boolean isOK(int xx, int yy) {
		if(0 <= xx && xx < w && 0 <= yy && yy < h) return true;
		return false;
	}

	private void debug(Object... o) {
		System.out.println("debug = " + Arrays.deepToString(o));
	}

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