Atcoder競プロ典型 90 問 043 - Maze Challenge with Lack of Sleep(★4)を ダイクストラ法で解く
この問題は、BFSで考えるのが適当です。それをダイクストラ法で解きます
公式解説
https://twitter.com/e869120/status/1394787605099601923
題意
- 迷路があり、任意のスタートとゴール(s,tとします)があります。sからtに移動したいです。
- 最初どの方向を向いていても良く、ゴール時にどの方向でも良いです。
- 向いている向きに進むのコスト0です。向きを変えるコストを1とします。
- sからtに向かう最小コストを求めてください。
- 制約: 迷路の大きさ$h \times w$ で、それぞれ1000以下
こう考えた
ダイクストラ法で以下のように考えました。
左の迷路があったとき、
- 右のように4つの{上, 右, 下, 左}にしか移動できない迷路を考えます。それぞれ移動できる先が壁でなければあるマスから移動先に対してcost 0の辺を張ります。
- 4つの迷路は各迷路の同じマス同士でcost 1の辺を張ります。例えば、上に移動できる迷路の$(0,0)$は、{右, 下, 左}の迷路にcost 1で移動できます。これは両方向に辺を張ります。
- スタートとゴールの向きを(無視することを)考慮するため、特別な辺s, tを作ります。
- sはcost 0でスタートの各向きのマスに対して計4本辺を張ります
- tは同様に、各向きのマスから辺を張ります
- s->tにダイクストラ法で最短経路を求めます
実装について
$1000 \times 1000$は一見小さいように見えますが、$1e6$と意外に多いです。最大の必要な辺と頂点の数を考察してみましょう。
- 頂点は各マスであるため$1e6 + 2$必要になります
- 辺は約$1.6 \times 1e7$必要になります。
- s,tからの各辺は8本です(ほぼ無視できます)
- 各マスの向き間の辺は$1.2 \times 1e7 $本です。各マスは有向辺で各向きと張るためです。
- 各向きの迷路の辺は最大で約$1e6$のため、合計で$4e6$本必要になります
Pythonでは制限時間内に解くのは難しいです。
実装
特に注意する点はない、のですが、辺や頂点が多いので、適切な型でリソースを確保しましょう。(例えば、ダイクストラの各ノードのコストはintの範囲に収まりますが、shortの範囲には収まりません。long longでは過剰です)
void solve(){
int h, w; cin >> h >> w;
int r1,c1,r2,c2; cin >> r1>>c1>>r2>>c2;
--r1; --r2; --c1; --c2;
char maze[h*w];
memset(maze, 0, sizeof(maze));
REP(hh, h) REP(ww, w) cin >> maze[hh*w + ww];
int masu = h*w; // この迷路のマス数
dijkstra<int> dj(4*masu + 2);
// 各向き間の辺を張る
REP(ind, masu) {
FOR(i, 0, 4) {
FOR(j, i + 1, 4) {
dj.makeEdge(masu*i + ind, masu*j + ind, 1);
dj.makeEdge(masu*j + ind, masu*i + ind, 1);
}
}
}
int s, t;
s = masu*4; // start node
t = masu*4 + 1; // goal node
// s -> startnodeにcost0で辺を貼る
FOR(i, 0, 4) dj.makeEdge(s, masu * i + (w * r1) + c1, 0);
// goalnode -> tにcost0で辺を張る
FOR(i, 0, 4) dj.makeEdge(masu * i + (w * r2) + c2, t, 0);
int nh, nw;
// 0:up, 1: right, 2:down, 3:left の レイヤだとする
REP(hh, h){
REP(ww, w){
// 上にマスがあり && 上が"."なら、 cost0で辺を張る
nh = hh - 1; nw = ww;
if(0 <= nh && nh < h && 0 <= nw && nw < w && maze[w*nh+nw] == '.') dj.makeEdge(masu*0 + w * hh + ww, masu*0 + w * nh + nw, 0);
nh = hh; nw = ww + 1;
if(0 <= nh && nh < h && 0 <= nw && nw < w && maze[w*nh+nw] == '.') dj.makeEdge(masu*1 + w * hh + ww, masu*1 + w * nh + nw, 0);
nh = hh + 1; nw = ww;
if(0 <= nh && nh < h && 0 <= nw && nw < w && maze[w*nh+nw] == '.') dj.makeEdge(masu*2 + w * hh + ww, masu*2 + w * nh + nw, 0);
nh = hh; nw = ww - 1;
if(0 <= nh && nh < h && 0 <= nw && nw < w && maze[w*nh+nw] == '.') dj.makeEdge(masu*3 + w * hh + ww, masu*3 + w * nh + nw, 0);
}
}
// s->tでダイクストラする
dj.solve(s, t);
// 制約上、この問題ではunreachはないが一応。
if(dj.cost[t] == dj.INF) cout << -1 << "\n";
else cout << dj.cost[t] << "\n";
}
Author And Source
この問題について(Atcoder競プロ典型 90 問 043 - Maze Challenge with Lack of Sleep(★4)を ダイクストラ法で解く), 我々は、より多くの情報をここで見つけました https://qiita.com/recuraki/items/c2dd293a80ddc7d53be4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .