NoiminのNoise

競技プログラミング (多め) とWeb (たまに) ,自然言語処理 (ブログではまだ)。数式の書き方を一気に KaTeX に変えようとして記事を全削除してインポートし直すなどしたので,過去にブックマークされた記事は URL が変わってしまっている可能性があります…….

Mujin Programming Challenge 2018 E - 迷路

E - 迷路

問題概要

N行M列 ($ N, M \leq 1000$) のグリッド上をロボットがSからGまで移動するのに最短何秒かかるか求める. ただし,1マス上下左右に動くのにかかる時間は1秒で,毎秒動ける方向が決まっている.動ける方向は長さk ($ k \leq 100000$) の文字列dによって与えられる.いま,ロボットが動き出してからt秒後とすると,ロボットが動ける方向はdのt%k文字目 (0-indexed) で表される.

解法概要

「ロボットが動き出してからt秒のとき,次にロボットが1マス上 (下・左・右) に動けるのは何秒後か」をあらかじめ計算しておくことで,ダイクストラ法によって最短時間を求めることができる.ロボットの動ける方向はk秒周期で決まるので0秒目からk-1秒目までの「次にロボットが1マス上 (下・左・右) に動けるのは何秒後か」を保持しておけばよく,事前計算の最悪時間計算量は$ O \left( k \right)$ .ただし,d = UDRRLでt=4のときの "次に上に動ける時間" など,次に指定の方向に動くまでに t%k = 0 をまたぐ場合があるので,プログラムでは2k秒ぶんのループを回している.

この事前計算の結果をグラフの辺のコストとし,上下左右に隣接しているマス同士にエッジが張られていると考え,ダイクストラ法を適用する.ダイクストラ法の最悪時間計算量は$ O \left( \left(V+E\right) \log V \right)$ (V,Eはそれぞれ頂点,辺の数) なので,制限時間の2秒には間に合う.

ソースコード

#include <iostream>
#include <queue>
#include <string>
#include <utility>

using namespace std;

typedef pair<int, int> Pii;
typedef long long ll;

const int dyx[4][2] = {
    { 0, 1}, {-1, 0}, {0,-1}, {1, 0}
};
const char dyx_chars[4] = {'R', 'U', 'L', 'D'};
const ll INF = (1LL << 60);

int n,m,k;
string s[1001],d;
ll shortest[1001][1001], times[100000][4];

struct State {
    ll t, y, x;
    State(ll t, int y, int x): t(t), y(y), x(x) {}
    bool operator>(const State& s) const{
        return t > s.t;
    }
};

void calc_times() {
    fill(times[0], times[k-1]+4, -1);
    for(int j=0;j<4;++j) {
        if(d[k-1] == dyx_chars[j]) times[k-1][j] = 1;
    }
    for(int i=2*k-2;i>=0;--i) {
        for(int j=0;j<4;++j) {
            if(d[i%k] == dyx_chars[j]) {
                times[i%k][j] = 1;
            } else {
                if(times[(i+1)%k][j] == -1) {
                    times[i%k][j] = -1;
                } else {
                    times[i%k][j] = times[(i+1)%k][j] + 1;
                }
            }
        }
    }
}

void dijkstra(Pii start) {
    priority_queue<State, vector<State>, greater<State> > que;
    que.push(State(0LL, start.first, start.second));
    while(!que.empty()) {
        State st = que.top(); que.pop();
        if(shortest[st.y][st.x] < st.t) continue;
        for(int i=0;i<4;++i) {
            int yy = st.y + dyx[i][0], xx = st.x + dyx[i][1];
            if(yy < 0 || n <= yy || xx < 0 || m <= xx || s[yy][xx] == '#' || times[st.t%k][i] == -1) continue;
            if(shortest[yy][xx] > shortest[st.y][st.x] + times[st.t%k][i]) {
                shortest[yy][xx] = shortest[st.y][st.x] + times[st.t%k][i];
                que.push(State(shortest[yy][xx], yy, xx));
            }
        }
    }
}

int main() {
    cin >> n >> m >> k;
    cin >> d;
    Pii start, goal;
    fill(shortest[0], shortest[n-1]+m, INF);
    for(int i=0;i<n;++i) {
        cin >> s[i];
        for(int j=0;j<m;++j) {
            if(s[i][j] == 'S') {
                start = Pii(i,j);
                shortest[i][j] = 0;
            } else if(s[i][j] == 'G') {
                goal = Pii(i, j);
            }
        }
    }

    calc_times();
    dijkstra(start);

    cout << (shortest[goal.first][goal.second] == INF ? -1 : shortest[goal.first][goal.second]) << endl;
}

所感

本番ではDで詰まっていてEは見ることもしなかったけど,これは明らかにEの方が得意なタイプだったので見るべきだった.