D - 迷路からの脱出 / Escape from the Maze Editorial by admin
Claude 4.5 Opus概要
グリッド上でスタートからゴールまで移動する際に、壁を破壊する回数を最小化する問題です。通路への移動コストは \(0\)、壁への移動コストは \(1\) として最短経路を求めます。
考察
問題の本質
この問題を言い換えると、「通路を通るコストは \(0\)、壁を通るコストは \(1\) として、\((1,1)\) から \((N,M)\) への最小コスト経路を求める」問題になります。
素朴なアプローチとその問題点
ダイクストラ法を使えば解けますが、優先度付きキューを使用するため計算量が \(O(NM \log(NM))\) となり、やや重くなります。
より効率的な解法への気づき
辺の重みが \(0\) または \(1\) の2種類しかないことに注目します。このような場合、0-1 BFS という特殊なBFSを使うことで、通常のBFSと同じ \(O(NM)\) の計算量で最短経路を求められます。
アルゴリズム
0-1 BFS とは
辺の重みが \(0\) か \(1\) のみのグラフにおける最短経路問題を、deque(両端キュー)を使って効率的に解く手法です。
動作原理
通常のBFSでは距離が小さい順に頂点を処理しますが、0-1 BFSでは以下のルールで処理順序を制御します:
- コスト \(0\) の辺で移動した場合 → dequeの先頭に追加
- コスト \(1\) の辺で移動した場合 → dequeの末尾に追加
これにより、常に「現在の最小コストの頂点」から処理されることが保証されます。
具体例
. # .
. # .
. . .
この迷路で \((0,0)\) から \((2,2)\) への最短経路を考えます。
- 壁を破壊せずに通る経路(下→下→右→右):コスト \(0\)
- 壁を1つ破壊して通る経路(右→下→下→右):コスト \(1\)
0-1 BFSでは、コスト \(0\) の移動を優先的に処理するため、正しく最小コストが計算されます。
処理の流れ
- 距離配列
distを \(\infty\) で初期化し、dist[0][0] = 0とする - dequeにスタート地点 \((0, 0)\) を追加
- dequeが空になるまで以下を繰り返す:
- 先頭から頂点 \((x, y)\) を取り出す
- 隣接する4方向の各マス \((nx, ny)\) について:
- 新しい距離が現在の距離より小さければ更新
- 通路(コスト \(0\))なら先頭に、壁(コスト \(1\))なら末尾に追加
計算量
- 時間計算量: \(O(NM)\)
- 各マスは高々1回しか最短距離で処理されないため
- 空間計算量: \(O(NM)\)
- 距離配列とdequeの領域
実装のポイント
dequeの使い分け: Pythonでは
collections.dequeを使い、appendleft()で先頭に、append()で末尾に追加します。距離の更新条件:
new_dist < dist[nx][ny]のときのみ更新することで、同じマスを何度も処理することを防ぎます。座標の扱い: 問題文では1-indexedですが、実装では0-indexedで扱うと配列アクセスが楽になります。
方向ベクトル: 上下左右の4方向を配列
dx,dyで管理すると、コードがすっきりします。ソースコード
from collections import deque
def solve():
N, M = map(int, input().split())
grid = [input().strip() for _ in range(N)]
# 0-1 BFS を使用
# 通路への移動はコスト0、壁への移動はコスト1
INF = float('inf')
dist = [[INF] * M for _ in range(N)]
dist[0][0] = 0
# deque を使った 0-1 BFS
dq = deque()
dq.append((0, 0))
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
while dq:
x, y = dq.popleft()
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < N and 0 <= ny < M:
# 移動先が通路ならコスト0、壁ならコスト1
cost = 0 if grid[nx][ny] == '.' else 1
new_dist = dist[x][y] + cost
if new_dist < dist[nx][ny]:
dist[nx][ny] = new_dist
if cost == 0:
dq.appendleft((nx, ny))
else:
dq.append((nx, ny))
print(dist[N-1][M-1])
solve()
この解説は claude4.5opus によって生成されました。
posted:
last update: