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: Wrong Answer)

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;

public class Main {
	
	public static int[][] move_dir = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
	
	public static int H;
	public static int W;
	public static int[][] map;
	public static int[] ices;
	
	public static class UnionFind{
		int[] par;
		UnionFind(int n){
			par=new int[n];
			for(int i=0;i<n;i++)par[i]=i;
		}
		public int find(int x){
			if(par[x]==x)return x;
			return par[x]=find(par[x]);
		}
		public Boolean same(int x,int y){
			return find(x)==find(y);
		}
		public void unite(int x,int y){
			if(find(x)==find(y))return;
			par[find(y)]=find(x);
		}
	}
	
	public static int ans = 1000;
	public static boolean[][] is_visited = new boolean[12][12];
	
	public static int dps(int x, int y, int gx, int gy, int px, int py, int time, int[] using_ice){
		if(is_visited[y][x]){
			return 0;
		}
		
		if(time + Math.abs(x - gx) + Math.abs(y - gy) >= ans){
			return 0;
		}
		
		if(x == gx && y == gy){
			ans = time;
			return 0;
		}
		
		//check
		for(int[] move : move_dir){
			final int nx = x + move[0];
			final int ny = y + move[1];
			
			if(nx < 0 || nx >= W || ny < 0 || ny >= H){
				continue;
			}else if(px == nx && py == ny){
				continue;
			}else if(is_visited[ny][nx]){
				return 0;
			}
		}
		
		is_visited[y][x] = true;
		//moving
		for(int[] move : move_dir){
			final int nx = x + move[0];
			final int ny = y + move[1];
			
			if(nx < 0 || nx >= W || ny < 0 || ny >= H){
				continue;
			}else if(map[ny][nx] == Integer.MAX_VALUE){
				continue;
			}else if(is_visited[ny][nx]){
				continue;
			}
			
			if(map[ny][nx] > 0){
				if(ices[map[ny][nx]] / 2 < using_ice[map[ny][nx]] + 1){
					continue;
				}else{
					using_ice[map[ny][nx]]++;
				}
			}
			
			dps(nx, ny, gx, gy, x, y, time + 1, using_ice);
			
			if(map[ny][nx] > 0){
				using_ice[map[ny][nx]]--;
			}
		}
		is_visited[y][x] = false;
		
		return 0;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		while (true) {
			W = sc.nextInt();
			H = sc.nextInt();
			
			if(W == 0 && H == 0){
				break;
			}
			
			map = new int[H][W];
			int sx = -1, sy = -1;
			int gx = -1, gy = -1;
			for(int i = 0; i < H; i++){
				char[] in = sc.next().toCharArray();
				
				for(int j = 0; j < W; j++){
					if(in[j] == 'S'){
						sx = j;
						sy = i;
						map[i][j] = 0;
					}else if(in[j] == 'G'){
						gx = j;
						gy = i;
						map[i][j] = 0;
					}else if(in[j] == 'X'){
						map[i][j] = 1;
					}else if(in[j] == '#'){
						map[i][j] = Integer.MAX_VALUE;
					}else{
						map[i][j] = 0;
					}
				}
			}
			
			int number = 1;
			for(int y = 0; y < H; y++){
				for(int x = 0; x < W; x++){
					if(map[y][x] <= 0 || map[y][x] == Integer.MAX_VALUE){
						continue;
					}
					
					if(x == 0 && y == 0){
						map[y][x] = number++;
					}else if(x == 0){
						if(map[y-1][x] > 0 && map[y-1][x] != Integer.MAX_VALUE){
							map[y][x] = map[y-1][x];
						}else{
							map[y][x] = number++;
						}
					}else if(y == 0){
						if(map[y][x-1] > 0 && map[y][x-1] != Integer.MAX_VALUE){
							map[y][x] = map[y][x-1];
						}else{
							map[y][x] = number++;
						}
					}else{
						final int left = map[y][x-1] <= 0 ? Integer.MAX_VALUE : map[y][x-1];
						final int up = map[y-1][x] <= 0 ? Integer.MAX_VALUE : map[y-1][x];
						final int min = Math.min(left, up);
						
						if(min == Integer.MAX_VALUE){
							map[y][x] = number++;
						}else{
							map[y][x] = min;
						}
					}
				}
			}
			
			UnionFind uf = new UnionFind(145);
			for(int y = 0; y < H; y++){
				for(int x = 0; x < W; x++){
					if(map[y][x] <= 0 || map[y][x] == Integer.MAX_VALUE){
						continue;
					}
					
					if(x != 0 && map[y][x-1] > 0 && map[y][x-1] != Integer.MAX_VALUE){
						if(map[y][x] < map[y][x-1]){
							uf.unite(map[y][x], map[y][x-1]);
						}else{
							uf.unite(map[y][x-1], map[y][x]);
						}
					}
					
					if(y != 0 && map[y-1][x] > 0 && map[y-1][x] != Integer.MAX_VALUE){
						if(map[y][x] < map[y-1][x]){
							uf.unite(map[y][x], map[y-1][x]);
						}else{
							uf.unite(map[y-1][x], map[y][x]);
						}
					}
					
					map[y][x] = uf.find(map[y][x]);
					if(x != 0 && map[y][x-1] > 0 && map[y][x-1] != Integer.MAX_VALUE){
						map[y][x-1] = uf.find(map[y][x-1]);
					}
					if(y != 0 && map[y-1][x] > 0 && map[y-1][x] != Integer.MAX_VALUE){
						map[y-1][x] = uf.find(map[y-1][x]);
					}
				}
			}
			
			Set<Integer> hash = new HashSet<Integer>();
			for(int y = 0; y < H; y++){
				for(int x = 0; x < W; x++){
					if(map[y][x] <= 0 || map[y][x] == Integer.MAX_VALUE){
						continue;
					}
					hash.add(map[y][x]);
				}
			}
			hash.add(0);
			
			ArrayList<Integer> list = new ArrayList<Integer>(hash);
			Collections.sort(list);
			
			ices = new int[hash.size()];
			
			for(int y = 0; y < H; y++){
				for(int x = 0; x < W; x++){
					if(map[y][x] <= 0 || map[y][x] == Integer.MAX_VALUE){
						continue;
					}
					map[y][x] = Collections.binarySearch(list, map[y][x]);
					ices[map[y][x]]++;
				}
			}
			
			/*
			for(int y = 0; y < H; y++){
				for(int x = 0; x < W; x++){
					if(y == sy && x == sx){
						System.out.printf("%s", "_S");
					}else if(y == gy && x == gx){
						System.out.printf("%s", "_G");
					}else if(map[y][x] <= 0 || map[y][x] == Integer.MAX_VALUE){
						System.out.printf("%s", map[y][x] == 0 ? "  " : "XX");
					}else{
						System.out.printf("%02d", map[y][x]);
					}
				}
				System.out.println();
			}
			System.out.println(Arrays.toString(ices));
			System.out.println("--------------------");
			*/
			ans = 1000;
			for(int y = 0; y < H; y++){
				for(int x = 0; x < W; x++){
					is_visited[y][x] = false;
				}
			}
			
			dps(sx, sy, gx, gy, -1, -1,  0, new int[hash.size()]);
			System.out.println(ans);
			
		}
	}

}