幅優先探索(BFS)の練習としてatCoderの問題を解いた
問題は「第三回アルゴリズム実技検定」のG問題「グリッド金移動」
(https://atcoder.jp/contests/past202005-open/tasks/past202005_g)
問題の把握
移動法に癖がある最短経路探索の問題
グラフとして考えると、グリッドのサイズをKとして、頂点の数K2で、辺の数はせいぜい 8K。
BFSは頂点の数をN、辺の数をMとして、計算量が O(N + M) 。
よって今回は頂点数が支配的になりO(K2)
題意から障害物とゴールの座標の外側である
-201 <= x <= 201, -201 <= y <= 201
を考えればよく、403 * 403 の二次元配列を考えればいい。
pythonの計算回数の目安は10**7くらいらしいので、余裕をもってクリアしている。
実装
シンプルに実装する
from collections import deque
N, X, Y = list(map(int, input().split()))
# 定数
gsize = 403
st_index = 201
# 座標を配列のインデックスに合わせる
X += st_index
Y += st_index
# 迷路を表す配列(Falseで初期化)
S = []
for i in range(gsize):
S.append([False] * gsize)
# 障害物のマスをTrueにする
for i in range(N):
x, y = list(map(int, input().split()))
# 座標を配列のインデックスに合わせる
x += st_index
y += st_index
S[x][y] = True
# 距離管理用の配列(-1で初期化)
dist = []
for i in range(gsize):
dist.append([-1] * gsize)
Q = deque()
# 原点の距離を0で更新してキュー
dist[st_index][st_index] = 0
Q.append([st_index, st_index])
while len(Q) > 0:
i, j = Q.popleft()
# 6方向を確認する
for i2, j2 in [
[i + 1, j + 1],
[i, j + 1],
[i - 1, j + 1],
[i + 1, j],
[i - 1, j],
[i, j - 1],
]:
# インデックス外は無視
if not (0 <= i2 <= gsize - 1 and 0 <= j2 <= gsize - 1):
continue
# 障害物は無視
if S[i2][j2]:
continue
# 未訪問なら距離を更新してキュー
if dist[i2][j2] == -1:
dist[i2][j2] = dist[i][j] + 1
Q.append([i2, j2])
# 距離を出力
print(dist[X][Y])
ACできた。pypyだと3倍くらい速かったのでpypyで提出するのがよさそう。
まとめ
テキスト通りの実装ができた。何日か連続して解いて手に覚えさせたい。