AOJ0247 Ice Maze
問題リンク Ice Maze
- 解法
色々アプローチを試し、最終的に通ったのは反復深化法でした。
最初に氷をグルーピングしておきます。
ゴールから各マスへの最短距離を調べます。このとき、氷は考慮しません。この距離が、(i, j)からゴールへ辿り着くために必要なステップ数の最小値となります。
ここまで準備ができたらDFSによる探索を開始します。
ゴールへ到着するステップ数LIMITを1から試し、最初に成功したLIMITが答えになります。
探索中では次の枝刈りをします。
LIMIT < 現在のステップ数 + ゴールまでの最短距離のとき
直前にいたマスを除き、近傍4マスの中に訪問済みのマスがあるとき(無駄な遠回りをしていることになります)
以上で通りました。
※余談
このアプローチの直前まで考えていたのが、反復深化法を使わずに上とまったく同じDFSをする方法でした。自分で作ったテストケース全てに、ほぼ0秒で答えを出したので行けると思いましたがTLEでした。反復深化を使うと、使わないときより時間がかかるのでダメかと思いましたがAcceptしました。TLEしているテストケースが知りたいところです
※おまけ
自分で作ってみたテストケースです
12 12 .XXX..#G.X.X .X..X.SX#X#X XX#X.#X..X.X .X#XXX.X...X XX#X#.X.#.#X ..X.XX#.XX.X XX#X#XXX.#.. .X.X..X#.XX. .#.....#.X.. .X.....#X... XX######X#.. .X.......... 12 12 S........... .##########. .#.......... .#........#. .#####...... .#...#....#. .#.#.#...... .#.#.#....#. .#.#.#...... .#.#.#....#. .#.#.#####.X ...#.......G 12 12 S........... #X#X#X#X#X#. .X.X.X.X.X.X .X#X#X#X#X#X .X.......... #X#X#X#X#X#. .X.X.X.X.X.X .X#X#X#X#X#X .X.......... #X#X#X#X#X#X .X.X.X.X.X.X G#.#.#.#.#XX 12 12 .X.......#.G .X.......XXX XSX......#.. XXX......XXX #X.......#.. #X#......XXX #X#......#.. #X#########. .X.XX...XXXX ....#XX..#.# ...XX.#.#... .XX..XXX.... 0 0 //answer 46 34 77 42
- ソース
import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; //Ice Maze public class AOJ0247 { int w, h, si, sj, gi, gj, INF = 1<<29, res; int[][] d = {{-1,0},{0,1},{1,0},{0,-1}}; int[] ice, hit; int[][] id, dist; char[][] m; boolean[][] v; void mark(int i, int j, int x){ id[i][j] = x; ice[x]++; for(int k=0;k<4;k++){ int ni = i+d[k][0], nj = j+d[k][1]; if(m[ni][nj]=='X'&&id[ni][nj]==0)mark(ni, nj, x); } } boolean f(int i, int j, int depth, int pi, int pj){ if(res < depth+dist[i][j])return false; if(gi==i && gj==j){ return true; } for(int k=0;k<4;k++){ int ni = i+d[k][0], nj = j+d[k][1]; if(ni!=pi&&nj!=pj&&v[ni][nj])return false; } for(int k=0;k<4;k++){ int ni = i+d[k][0], nj = j+d[k][1]; if(!v[ni][nj]&&m[ni][nj]!='#'){ if(ice[id[ni][nj]]>>1 >= hit[id[ni][nj]]+1){ hit[id[ni][nj]]++; v[ni][nj] = true; if(f(ni, nj, depth+1, i, j))return true; v[ni][nj] = false; hit[id[ni][nj]]--; } } } return false; } void run(){ Scanner sc = new Scanner(System.in); ice = new int[144]; hit = new int[144]; dist = new int[14][14]; m = new char[14][14]; v = new boolean[14][14]; for(;;){ w = sc.nextInt(); h = sc.nextInt(); if((w|h)==0)break; for(int i=0;i<h+2;i++)for(int j=0;j<w+2;j++){ dist[i][j] = INF; v[i][j] = false; m[i][j] = '#'; } for(int i=1;i<=h;i++){ char[] c = sc.next().toCharArray(); for(int j=1;j<=w;j++){ m[i][j] = c[j-1]; if(m[i][j]=='S'){ m[i][j] = '.'; si = i; sj = j; } if(m[i][j]=='G'){ m[i][j] = '.'; gi = i; gj = j; } } } Arrays.fill(ice, 0); ice[0] = INF; id = new int[h+2][w+2]; int ID = 1; for(int i=1;i<=h;i++)for(int j=1;j<=w;j++){ if(m[i][j]!='X'||id[i][j]>0)continue; mark(i, j, ID); if(ice[ID]==1){ m[i][j] = '#'; } ID++; } dist[gi][gj] = 0; Queue<int[]> que = new LinkedList<int[]>(); que.add(new int[]{gi, gj}); while(!que.isEmpty()){ int V[] = que.poll(); int pi = V[0], pj = V[1]; for(int k=0;k<4;k++){ int ni = pi+d[k][0], nj = pj+d[k][1]; if(m[ni][nj]!='#'&&dist[ni][nj]==INF){ dist[ni][nj] = dist[pi][pj]+1; que.add(new int[]{ni, nj}); } } } res = 0; Arrays.fill(hit, 0); v[si][sj] = true; while(!f(si, sj, 0, -1, -1)){ Arrays.fill(hit, 0); for(int i=0;i<h+2;i++)for(int j=0;j<w+2;j++)v[i][j]=false; v[si][sj] = true; res++; } System.out.println(res); } } public static void main(String[] args) { new AOJ0247().run(); } }