Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

AOJ 0247 - Ice Maze

問題文 : AOJ 0247

解法 :


  • 基本的には深さ優先探索を行う.

  • ただし, それだけでは時間制限に間に合わないので, 反復深化深さ優先探索(IDDFS)を行う.

  • 具体的には, 下界uを決めて, uよりコストがかかることがわかればuを増やすという事を行う. 下界から決めているので, はじめてゴールに到達できた時が最適解である.

  • それ以外にも, マンハッタン距離を使った枝刈り(ソース参照)などを行うと高速でプログラムが動作する.

コード :

#include <cstdio>
#include <cmath>
#include <cstring>
  
using namespace std;
  
 
int W, H;
  
char map[16][16];
char g[16][16];
bool v[16][16];
  
int maxIce[128];
int ice[128];
  
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
 
int ans;
int px, py;
int sx, sy, gx, gy;
  
bool getMin(int ty, int tx, int turn)
{
      
    if (turn + abs(ty - gy) + abs(tx - gx) >= ans){
        return (false);
    }
      
    if (ty == gy && tx == gx){
        ans = turn;
        return (true);
    }
      
    for (int i = 0; i < 4; i++){
        int mx = tx + dx[i], my = ty + dy[i];
        if (0 <= mx && mx < W && 0 <= my && my < H && px != mx && py != my && v[my][mx]){
            return (false);
        }
    }
      
    for (int i = 0; i < 4; i++){
        int mx = tx + dx[i], my = ty + dy[i];
        px = tx, py = ty;
        if (0 <= mx && mx < W && 0 <= my && my < H && map[my][mx] != '#' && !v[my][mx]){
            if (g[my][mx] == -1){
                v[my][mx] = true;
                if (getMin(my, mx, turn + 1)) return (true);
                v[my][mx] = false;
            }
            else if (maxIce[g[my][mx]] >= ice[g[my][mx]] + 1){
                ice[g[my][mx]]++;
                v[my][mx] = true;
                if (getMin(my, mx, turn + 1)) return (true);
                v[my][mx] = false;
                ice[g[my][mx]]--;
            }
        }
    }
     
    return (false);
}
  
void label(int ty, int tx, int p)
{
    g[ty][tx] = p;
    maxIce[p]++;
      
    for (int i = 0; i < 4; i++){
        int mx = tx + dx[i], my = ty + dy[i];
          
        if (0 <= mx && mx < W && 0 <= my && my < H &&
                    map[my][mx] == 'X' && g[my][mx] == -1){
            label(my, mx, p);
        }
    }
}
  
int main()
{
    int idx;
      
    while (1){
        scanf("%d %d", &W, &H);
          
        if (W + H == 0){
            break;
        }
          
        for (int i = 0; i < H; i++){
            scanf("%s", map[i]);
            for (int j = 0; j < W; j++){
                if (map[i][j] == 'S'){
                    map[i][j] = '.';
                    sx = j, sy = i;
                }
                if (map[i][j] == 'G'){
                    map[i][j] = '.';
                    gx = j, gy = i;
                }
            }
        }
          
        memset(g, -1, sizeof(g));
        memset(maxIce, 0, sizeof(maxIce));
          
        idx = 0;
        for (int i = 0; i < H; i++){
            for (int j = 0; j < W; j++){
                if (map[i][j] == 'X' && g[i][j] == -1){
                    label(i, j, idx);
                    maxIce[idx++] /= 2;
                }
            }
        }
          
        memset(ice, 0, sizeof(ice));
        memset(v, 0, sizeof(v));
        px = py = -1;
        v[sy][sx] = true;
         
        ans = 0;
        while (!getMin(sy, sx, 0)){
            memset(ice, 0, sizeof(ice));
            memset(v, 0, sizeof(v));
            px = py = -1;
            v[sy][sx] = true;
            ans++;
        }
         
        printf("%d\n", ans);
    }
      
    return (0);
}