競技プログラミング日記

主に AtCoder の記事です

第11回アルゴリズム実技検定 I問題

 

PAST011I

\(H,W \leq 50\) と小さい. 盤面は,(人の座標,荷物の座標) さえ分かっていれば,後は何とかなる.

実装
(人の座標,荷物の座標)を持ちながらBFS. グラフの頂点の個数は \(O(\ (H \times W)^{2}\ )\), 辺の個数は,頂点の4倍. よって,合計で \(O(5(H \times W)^{2})\) より, \(3.125 \cdot 10^{7}\) 程度.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

ll dis[55][55][55][55];
bool vis[55][55][55][55];

int main() {
  ll h,w; cin >> h >> w;
  vector<string> s(h);
  cinv(s);

  rep(x,h) rep(y,w) rep(a,h) rep(b,w){
    dis[x][y][a][b] = INFL;
  }

  // remark : s[x][y] != 'a' は不要
  auto valid = [&](ll x, ll y) -> bool {
    return in(x,h) && in(y,w) && (s[x][y]!='#');
  };
 

  using T = tuple<ll,ll,ll,ll>;
  queue<T> que;

  auto push = [&](ll x, ll y, ll a, ll b, ll d) -> void {
    // if(!valid(x,y)) return; // rem : valid のタイミング
    if(vis[x][y][a][b]) return;
    vis[x][y][a][b] = true;
    que.push({x,y,a,b});
    dis[x][y][a][b] = d;
  };

  pll src, tar, car; // cargo
  rep(x,h) rep(y,w){
    if(s[x][y] == 's') src.first = x, src.second = y;
    if(s[x][y] == 'g') tar.first = x, tar.second = y;
    if(s[x][y] == 'a') car.first = x, car.second = y;
  }

  push(src.first, src.second, car.first, car.second, 0);
  while(que.size()){
    auto [x,y,a,b] = que.front(); que.pop();
    ll d = dis[x][y][a][b];

    vll dx = {-1,0,1,0};
    vll dy = {0,-1,0,1};
    rep(i,4){
      ll nx = x + dx[i];
      ll ny = y + dy[i];

      pll px = {nx, ny};
      pll pa = {a, b};
      if(px != pa){
        if(valid(nx,ny)) push(nx, ny, a, b, d+1);
      }else{
        // 2マス先
        ll nx2 = x + 2*dx[i];
        ll ny2 = y + 2*dy[i];

        if(valid(nx2, ny2)) push(nx, ny, nx2, ny2, d+1);
      }
    }
  }

  ll ans = INFL;
  rep(x,h) rep(y,w){
    chmin(ans, dis[x][y][tar.first][tar.second]);
  }
  if(ans >= INFL) cout << -1 << endl;
  else cout << ans << endl;

  return 0;
}