1376 -- Robot

http://acm.pku.edu.cn/JudgeOnline/problem?id=1376

概要

直径 1.6 メートルのロボットがグリッドの交点上を動く.グリッド 1 マスの一辺は 1 メートル.
障害物に触れてはいけない.
ロボットは

  • 左に 90 度回転
  • 右に 90 度回転
  • 前に 1 マス進む
  • 前に 2 マス進む
  • 前に 3 マス進む

というコマンドをそれぞれコスト 1 で行うことができる.
スタート地点とゴール地点に辿りつくまでの最小コストはいくつか.

解法

実装系.M, N <= 50 だし普通に BFS.
1.6 メートルという半端な直径だが,普通に 4 マスを消費すると考えればいい.
方向も考える必要があるので,到達済みのノードは visited[M][N][4] のように管理する.
例えば 2 マス進んだときに障害物に触れたら当然 3 マス目も行けないことに注意.これはサンプルでも確かめることができる.
キューから取りだしたときに visited を更新する実装だとキューが溢れて MLE.
ゴールに辿りつけないときは -1 を出力することに注意.ここでまず間違えた.
一番端には行けないことに注意.つまり座標で言うと (0,n) とか (m,0) とか (m,N) とか (M,n) とかに行けない.ここでも間違えてた.

コード

#include <iostream>
#include <vector>
#include <queue>
#include <complex>
using namespace std;
typedef complex<int> P;

struct State {
  P pos;
  int dir;
  int depth;
  State(P pos, int dir, int depth) : pos(pos), dir(dir), depth(depth) {}
};

int main()
{
  int M, N;
  while (cin >> M >> N && M != 0) {
    vector<vector<bool> > block(M, vector<bool>(N));
    for (int i = 0; i < M; i++) {
      for (int j = 0; j < N; j++) {
        int x;
        cin >> x;
        block[i][j] = x == 1;
      }
    }
    string dir;
    P start, goal;
    cin >> start.real() >> start.imag() >> goal.real() >> goal.imag() >> dir;
    int d;
    if (dir == "north") {
      d = 0;
    } else if (dir == "west") {
      d = 1;
    } else if (dir == "south") {
      d = 2;
    } else {
      d = 3;
    }

    vector<vector<vector<bool> > > visited(M, vector<vector<bool> >(N, vector<bool>(4, false)));
    queue<State> q;
    q.push(State(start, d, 0));
    visited[start.real()][start.imag()][d] = true;
    while (!q.empty() && q.front().pos != goal) {
      State s = q.front();
      q.pop();
      for (int g = 1; g <= 3; g++) {
        static const int d_m[] = {-1, 0, 1, 0};
        static const int d_n[] = {0, -1, 0, 1};
        int m = s.pos.real() + d_m[s.dir]*g;
        int n = s.pos.imag() + d_n[s.dir]*g;
        if (m > 0 && m < M && n > 0 && n < N && !visited[m][n][s.dir]) {
          if (block[m-1][n] || block[m][n-1] || block[m][n] || block[m-1][n-1])
            break;

          visited[m][n][s.dir] = true;
          q.push(State(P(m, n), s.dir, s.depth+1));
        }
      }
      if (!visited[s.pos.real()][s.pos.imag()][(s.dir+1)%4]) {
        q.push(State(s.pos, (s.dir+1)%4, s.depth+1));
        visited[s.pos.real()][s.pos.imag()][(s.dir+1)%4] = true;
      }
      if (!visited[s.pos.real()][s.pos.imag()][(s.dir+3)%4]) {
        q.push(State(s.pos, (s.dir+3)%4, s.depth+1));
        visited[s.pos.real()][s.pos.imag()][(s.dir+3)%4] = true;
      }
    }
    if (q.empty()) {
      cout << -1 << endl;
    } else {
      cout << q.front().depth << endl;
    }
  }
  return 0;
}