E - 洞窟探検と地下水路 / Cave Exploration and Underground Waterways 解説 by admin
gpt-5.3-codexOverview
This is a “weighted shortest path” problem where moving to a V cell via normal movement is expensive (2 minutes), and movement within the same underground waterway costs 0 minutes.
We build a graph by collapsing each connected component of V into a single “relay node,” then find the shortest time using Dijkstra’s algorithm.
Analysis
The difficulty of this problem lies in the two types of movement involving V coexisting:
- Entering
Vvia normal movement: \(1 + 1 = 2\) minutes - When on a
Vcell, moving to anyVwithin the same underground waterway: \(0\) minutes
Key Insight 1: The 0-minute movement within waterways can be handled per connected component
Within the same underground waterway (connected component of V), any pair of V cells can effectively travel between each other in 0 minutes.
Therefore, we create a super vertex for each connected component and add edges:
Vcell → component vertex (weight 0)- component vertex →
Vcell (weight 0)
This naturally represents the warp within a component.
This eliminates the need to add 0-cost edges between all pairs of V within the same component, preventing an explosion in the number of edges.
Key Insight 2: Normal movement cost is determined by the type of the destination cell
According to the problem statement, normal movement in the four cardinal directions takes 1 minute, with an additional 1 minute if the destination is V.
Therefore, the weight of normal movement is:
- Destination is
V: 2 - Otherwise (
S,G,O): 1
(X cells are impassable in the first place.)
Why the naive approach is too slow
- Adding 0-cost edges from each
Vto all otherVcells in the same component results in a quadratic number of edges in the worst case, risking TLE/MLE. - BFS cannot be used directly because weights of 0, 1, and 2 coexist (0-1 BFS also doesn’t work due to the weight-2 edges).
Therefore, the natural approach is:
1. Find connected components of V
2. Introduce super vertices to keep the graph sparse
3. Apply Dijkstra’s algorithm
Algorithm
Preprocessing: Assign connected component IDs to
Vcells- Scan the entire grid and perform BFS (or DFS) from each unvisited
Vto label its component. - Record the component number in
comp_id[cell].
- Scan the entire grid and perform BFS (or DFS) from each unvisited
Build the augmented graph
- Normal cell vertices: \(0 \ldots N-1\) (\(N = H \times W\))
- Component super vertices: \(N \ldots N+C-1\) (\(C\) is the number of
Vcomponents) - For each passable cell
u, add normal movement edges to adjacent passable cellsv:- Weight \(2\) (if
visV) - Weight \(1\) (otherwise)
- Weight \(2\) (if
- If
uisV:u -> super(comp_id[u])with weight 0super(comp_id[u]) -> uwith weight 0
Dijkstra’s algorithm
- Compute shortest distances from the start
S. - If the distance to
Gis never updated, outputNO(unreachable); otherwise, output the distance value.
- Compute shortest distances from the start
Complexity
- Time complexity: \(O((N + E)\log(N+C))\)
where \(N = H \times W\).
Each cell has at most 4 adjacent edges, so \(E = O(N)\). The 0-weight edges betweenVcells and component vertices also total \(O(N)\).
Thus, the overall complexity is approximately \(O(N\log N)\). - Space complexity: \(O(N + E) = O(N)\)
Implementation Tips
Converting cell \((r, c)\) to a 1D ID
r*W+csimplifies the implementation.Using
passable(ch) = (ch != 'X')to unify passability checks reduces bugs.Note that normal movement cost is determined by the destination, not the current cell.
In Dijkstra’s algorithm, include
if d != dist[u]: continueto skip stale heap entries.Source Code
import sys
from collections import deque
def main():
input = sys.stdin.readline
H, W = map(int, input().split())
grid = [input().strip() for _ in range(H)]
N = H * W
def idx(r, c):
return r * W + c
start = goal = -1
for r in range(H):
row = grid[r]
for c, ch in enumerate(row):
if ch == 'S':
start = idx(r, c)
elif ch == 'G':
goal = idx(r, c)
# 1) Label connected components of V-cells
comp_id = [-1] * N
comp_count = 0
dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for r in range(H):
for c in range(W):
if grid[r][c] != 'V':
continue
i0 = idx(r, c)
if comp_id[i0] != -1:
continue
cid = comp_count
comp_count += 1
q = deque([i0])
comp_id[i0] = cid
while q:
v = q.popleft()
vr, vc = divmod(v, W)
for dr, dc in dirs:
nr, nc = vr + dr, vc + dc
if 0 <= nr < H and 0 <= nc < W and grid[nr][nc] == 'V':
ni = idx(nr, nc)
if comp_id[ni] == -1:
comp_id[ni] = cid
q.append(ni)
# 2) Build super graph:
# node 0..N-1 : cells
# node N..N+comp_count-1 : component supernodes
total_nodes = N + comp_count
adj = [[] for _ in range(total_nodes)]
def passable(ch):
return ch != 'X'
for r in range(H):
for c in range(W):
ch = grid[r][c]
if not passable(ch):
continue
u = idx(r, c)
# normal moves to neighbors
for dr, dc in dirs:
nr, nc = r + dr, c + dc
if 0 <= nr < H and 0 <= nc < W:
nch = grid[nr][nc]
if passable(nch):
v = idx(nr, nc)
w = 2 if nch == 'V' else 1
adj[u].append((v, w))
# zero-cost teleport via V component
if ch == 'V':
cid = comp_id[u]
snode = N + cid
adj[u].append((snode, 0))
adj[snode].append((u, 0))
# 3) Dijkstra
INF = 10**30
dist = [INF] * total_nodes
dist[start] = 0
import heapq
hq = [(0, start)]
while hq:
d, u = heapq.heappop(hq)
if d != dist[u]:
continue
if u == goal:
break
for v, w in adj[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(hq, (nd, v))
ans = dist[goal]
if ans >= INF:
print("NO")
else:
print(ans)
if __name__ == "__main__":
main()
This editorial was generated by gpt-5.3-codex.
投稿日時:
最終更新: