2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) - B: Buggy Robot ★★★☆☆

評価

良問だが若干実装疲れた.
初め, dijkstraに見えなかった.
解くのにそんなに時間がかからなかったし若干実装重いB問題かーくらいの感覚だったけど, 解いてる人数少なくて驚いた.

問題

迷路と操作列が与えられる.
操作列はUDLR(上下左右)からなる文字列である.
スタート地点にいるロボットが操作列に従って移動する.
ただし画面外に移動したりゴール後に移動したりしない.
操作列のいくつかを削除(無視)したり任意の操作を加えたりできる.
ロボットがゴールにたどり着くために操作を削除・追加する回数の最小値を求めよ.

入力例

3 3
R..
.#.
..E
LRDD

出力例

1

考察

dp[i][y][x] := 元の操作列[0,i)に変更を加えた操作列を実行したときに地点(x,y)に居るような変更回数の最小値
とすると,

  • 今の操作列に従う (コスト0, (i,y,x) → (i+1, y', x'))
  • 次の操作を無視する (コスト1, (i,y,x) → (i+1,y,x))
  • 操作を加える (コスト1, (i,y,x) → (i+1,y,x))

の遷移がある.
(i,y,x)を頂点とするとDAGとは限らず, また, 異なるコストの辺があるのでdijkstraする.

解答

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<ll,ll> P;

const ll linf = 1e18;

#define FOR(i,a,b) for(ll i = (a); i < (b); ++i)
#define RFOR(i,a,b) for(ll i = (b)-1; i >= (a); --i)
#define REP(i,n) FOR(i,0,n)
#define RREP(i,n) RFOR(i,0,n)
#define EACH(i,v) for(auto&& i : v)
#define ALL(v) (v).begin(),(v).end()
#define push_back pb

template<class T>
istream& operator>>(istream& is, vector<T>& v) {
    EACH(i, v) is >> i;
    return is;
}

vector<vector<vector<ll>>> dp;

struct Node {
    ll pos, x, y, cost;
};
bool operator>(const Node& n1, const Node& n2) {
    return n1.cost > n2.cost;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll h, w; cin >> h >> w;
    auto inRange = [&](ll x, ll y) {
        return 0 <= x && x < w && 0 <= y && y < h;
    };
    vector<string> m(h); cin >> m;
    string s; cin >> s;
    const ll n = s.size();
    dp.assign(n+1, vector<vector<ll>>(h, vector<ll>(w, linf)));
    priority_queue<Node, vector<Node>, greater<Node>> Q;
    ll gx, gy;
    REP(y, h) REP(x, w) {
        if (m[y][x] == 'R') {
            Q.push({0, x, y, 0});
            dp[0][y][x] = 0;
        }
        if (m[y][x] == 'E') {
            gx = x, gy = y;
        }
    }
    auto move = [&](ll x, ll y, char op) {
        if (x == gx && y == gy) return P(x, y);
        ll dx, dy;
        if (op == 'U') dx = 0, dy = -1;
        if (op == 'R') dx = 1, dy = 0;
        if (op == 'D') dx = 0, dy = 1;
        if (op == 'L') dx = -1, dy = 0;
        ll nx = x + dx, ny = y + dy;
        if ( !inRange(nx, ny) || m[ny][nx] == '#' ) return P(x, y);
        return P(nx, ny);
    };
    const string ops = "URDL";
    while ( !Q.empty() ) {
        Node node = Q.top(); Q.pop();
        ll pos = node.pos, x = node.x, y = node.y;
        ll cost = node.cost;
        if (cost > dp[pos][y][x]) continue;
        // cout << pos << " " << x << " " << y << " " << cost << " " << dp[pos][y][x] << endl;
        if (pos < n) {
            // follow
            P p = move(x, y, s[pos]);
            ll nx, ny; tie(nx, ny) = p;
            if (dp[pos][y][x] < dp[pos+1][ny][nx]) {
                dp[pos+1][ny][nx] = dp[pos][y][x];
                Q.push({pos+1, nx, ny, dp[pos+1][ny][nx]});
            }
            // ignore
            if (dp[pos+1][y][x] > dp[pos][y][x]+1) {
                dp[pos+1][y][x] = dp[pos][y][x]+1;
                Q.push({pos+1, x, y, dp[pos+1][y][x]});
            }
        }
        // give op
        EACH(op, ops) {
            P p = move(x, y, op);
            ll nx, ny; tie(nx, ny) = p;
            if (dp[pos][y][x]+1 < dp[pos][ny][nx]) {
                dp[pos][ny][nx] = dp[pos][y][x]+1;
                Q.push({pos, nx, ny, dp[pos][ny][nx]});
            }
        }
    }
    cout << dp[n][gy][gx] << endl;
}