E - 洞窟探検と地下水路 / Cave Exploration and Underground Waterways 解説 by admin
Claude 4.6 Opus (Thinking)Overview
Since you can move freely within a connected component of waterlogged passages (V) in 0 minutes, this problem is solved by building a graph where each connected component is compressed into a single node, then finding the shortest time using Dijkstra’s algorithm.
Analysis
Key Insight: A connected component of V can be treated as a single node
Within the same underground waterway (a connected component of V), you can move to any cell in 0 minutes by riding the water flow. In other words, once you enter a certain underground waterway, there is no need to distinguish which V cell you are currently at within that waterway.
Problem with the Naive Approach
If each cell is treated as a node and all 0-cost edges between V cells are added, then for a single underground waterway containing \(k\) cells, \(O(k^2)\) edges would be needed, causing TLE.
Solution: Graph Compression
Each underground waterway is merged into a single super node. This keeps the number of edges to \(O(H \times W)\).
Edge Cost Design
In the compressed graph, the following three types of edges are added:
| Edge Type | Cost | Reason |
|---|---|---|
| Non-V cell → Adjacent non-V cell | \(1\) | Normal movement |
| Non-V cell → Adjacent V component (super node) | \(2\) | Normal movement \(1\) + Additional \(1\) for entering V |
| V component (super node) → Adjacent non-V cell | \(1\) | 0-minute movement within component to adjacent cell + Normal movement \(1\) |
For example, when moving O → V → V → V → O, in the compressed graph this becomes O →(2)→ V component →(1)→ O, with a total cost of \(3\). This matches the original graph: cost \(2\) to enter the first V + \(0\) for movement within the component + cost \(1\) to exit the component.
Algorithm
- Build Connected Components: Perform BFS on
Vcells in the grid to find connected components, assigning an ID to each component. - Build Compressed Graph:
- Non-V passable cells (
S,G,O) remain as individual nodes. - Each
Vconnected component becomes a single super node (node number \(H \times W + \text{component ID}\)). - Add edges according to the table above. Duplicate edges are eliminated using a set.
- Non-V passable cells (
- Dijkstra’s Algorithm: Find the shortest distance from start
StoG. - If unreachable, output
NO.
Complexity
- Time Complexity: \(O(H \times W \times \log(H \times W))\)
- BFS for connected components: \(O(H \times W)\)
- Number of edges in the graph: \(O(H \times W)\)
- Dijkstra’s algorithm: \(O(E \log V) = O(H \times W \times \log(H \times W))\)
- Space Complexity: \(O(H \times W)\)
Implementation Notes
Super Node Numbering: Regular cell node numbers are \(r \times W + c\) (from \(0\) to \(H \times W - 1\)), and super nodes are \(H \times W + \text{component ID}\), ensuring no number collisions.
Eliminating Duplicate Edges: When a single
Vcomponent is adjacent to the same non-V cell at multiple locations, adding the same edge multiple times is wasteful, so asetis used to prevent duplicates. This keeps the number of edges at \(O(H \times W)\).Meaning of Edge Cost \(2\): The edge cost of \(2\) from a non-V cell to a
Vcomponent is the sum of “\(1\) minute for normal movement + additional \(1\) minute for entering a V cell.” Since movement within the component takes \(0\) minutes, once you reach the super node, you can be considered to be at anyVcell within the component.Source Code
import sys
from heapq import heappush, heappop
from collections import deque
def solve():
input_data = sys.stdin.buffer.read().decode()
tokens = input_data.split()
idx = 0
H = int(tokens[idx]); idx += 1
W = int(tokens[idx]); idx += 1
grid = []
for i in range(H):
grid.append(tokens[idx]); idx += 1
start = end = -1
for r in range(H):
for c in range(W):
if grid[r][c] == 'S':
start = r * W + c
elif grid[r][c] == 'G':
end = r * W + c
# Find V connected components
comp = [-1] * (H * W)
num_comp = 0
for r in range(H):
for c in range(W):
if grid[r][c] == 'V' and comp[r * W + c] == -1:
cid = num_comp
num_comp += 1
q = deque()
q.append((r, c))
comp[r * W + c] = cid
while q:
cr, cc = q.popleft()
for dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)):
nr, nc = cr + dr, cc + dc
if 0 <= nr < H and 0 <= nc < W and grid[nr][nc] == 'V' and comp[nr * W + nc] == -1:
comp[nr * W + nc] = cid
q.append((nr, nc))
# Build compressed graph
# Nodes: 0..H*W-1 for non-V passable cells, H*W..H*W+num_comp-1 for V components
total_nodes = H * W + num_comp
adj = [[] for _ in range(total_nodes)]
# Track which (comp_node, neighbor) edges we've already added to avoid duplicates
comp_to_cell_added = set()
cell_to_comp_added = set()
for r in range(H):
for c in range(W):
ch = grid[r][c]
if ch == 'X':
continue
if ch == 'V':
cid = comp[r * W + c]
comp_node = H * W + cid
for dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)):
nr, nc = r + dr, c + dc
if 0 <= nr < H and 0 <= nc < W:
nch = grid[nr][nc]
if nch in ('S', 'G', 'O'):
ncell = nr * W + nc
key1 = (comp_node, ncell)
if key1 not in comp_to_cell_added:
comp_to_cell_added.add(key1)
adj[comp_node].append((ncell, 1))
key2 = (ncell, comp_node)
if key2 not in cell_to_comp_added:
cell_to_comp_added.add(key2)
adj[ncell].append((comp_node, 2))
else:
# S, G, O
cell = r * W + c
for dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)):
nr, nc = r + dr, c + dc
if 0 <= nr < H and 0 <= nc < W:
nch = grid[nr][nc]
if nch in ('S', 'G', 'O'):
adj[cell].append((nr * W + nc, 1))
# V adjacency handled in V branch
# Dijkstra
INF = float('inf')
dist = [INF] * total_nodes
dist[start] = 0
heap = [(0, start)]
while heap:
d, u = heappop(heap)
if d > dist[u]:
continue
if u == end:
print(d)
return
for v, w in adj[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heappush(heap, (nd, v))
if dist[end] < INF:
print(dist[end])
else:
print("NO")
solve()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: