Official
D - アイスリンクの脱出 / Escape from the Ice Rink Editorial by admin
Qwen3-Coder-480B概要
アイスリンク上で、柱や壁にぶつかるまで一直線に滑る高橋君が、スタート地点 \((1, 1)\) からゴール地点 \((H, W)\) まで最短何回の移動で到達できるかを求める問題。
考察
この問題では、通常のグリッド上の移動ではなく、「滑る」動きを考える必要があります。つまり、上下左右のいずれかの方向を選んで動き出すと、柱またはマップの端にぶつかるまで止まらずに進みます。
この「滑る」性質により、単純にBFSで1マスずつ探索しても正しい答えは得られません。例えば、あるマスから右に動いても、実際に停止するのはずっと先かもしれません。
また、同じマスを何度も通る可能性があるため、訪問済みかどうかを適切に管理しないと無限ループに陥ります。
このような状況では、「各マスに到達するための最小移動回数」を記録しながらBFSを行うのが有効です。そして、各ステップで「滑って止まる地点」までシミュレートして次の候補とします。
アルゴリズム
本問題は、0-1 BFSやダイクストラ法のような考え方を応用したBFSで解くことができます。
具体的な手順は以下の通りです:
- スタート位置 \((0, 0)\) をキューに入れ、そのマスへの移動回数を0として記録。
- キューから現在位置を取り出し、4方向それぞれに対して以下を実施:
- 方向を決めたら、柱または境界にぶつかるまで滑り続ける。
- 最終的な停止位置が未訪問であれば、そこまでの移動回数(現在+1)を記録し、キューに追加。
- ゴール \((H-1, W-1)\) に到達したらその時の移動回数を出力。
- 探索が終わっても到達できなければ
-1を出力。
このとき、滑る処理はwhileループを使ってシミュレートします。
計算量
- 時間計算量: \(O(H \times W)\)
各マスにつき最大4方向を試す必要があり、全体で \(H \times W\) マスなので、定数倍を考慮しても十分間に合う範囲です。 - 空間計算量: \(O(H \times W)\)
訪問済みフラグやBFSのキューのために \(H \times W\) の二次元配列を使用します。
実装のポイント
- 座標系は入力が1-indexedなので、内部では0-indexedに変換しておくと扱いやすい。
- 滑る処理では、「次に進む予定のマス」が壁でも柱でもないことを確認してから実際に更新する。
- 訪問済み判定は、単なるsetより「距離を記録する配列」を使った方が効率的かつ実装しやすい。
- ゴールにたどり着いたら即座に出力を返すことで無駄な探索を省ける。
## ソースコード
```python
from collections import deque
def main():
import sys
input = sys.stdin.read
data = input().split()
H = int(data[0])
W = int(data[1])
N = int(data[2])
walls = set()
for i in range(N):
r = int(data[3 + 2*i]) - 1
c = int(data[4 + 2*i]) - 1
walls.add((r, c))
start = (0, 0)
goal = (H-1, W-1)
# Directions: up, down, left, right
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
# BFS
queue = deque()
queue.append((start[0], start[1], 0)) # (row, col, moves)
visited = [[-1]*W for _ in range(H)]
visited[start[0]][start[1]] = 0
while queue:
r, c, moves = queue.popleft()
for dr, dc in directions:
nr, nc = r, c
# Slide until hit wall or pillar
while True:
tr = nr + dr
tc = nc + dc
# Check boundaries
if not (0 <= tr < H and 0 <= tc < W):
break # Hit the edge of the grid
# Check pillar
if (tr, tc) in walls:
break # Hit a pillar
nr, nc = tr, tc
# If not visited or found shorter path
if visited[nr][nc] == -1:
visited[nr][nc] = moves + 1
if (nr, nc) == goal:
print(moves + 1)
return
queue.append((nr, nc, moves + 1))
print(-1)
if __name__ == "__main__":
main()
この解説は qwen3-coder-480b によって生成されました。
posted:
last update: