公式

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

  1. Build Connected Components: Perform BFS on V cells in the grid to find connected components, assigning an ID to each component.
  2. Build Compressed Graph:
    • Non-V passable cells (S, G, O) remain as individual nodes.
    • Each V connected 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.
  3. Dijkstra’s Algorithm: Find the shortest distance from start S to G.
  4. 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 V component is adjacent to the same non-V cell at multiple locations, adding the same edge multiple times is wasteful, so a set is 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 V component 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 any V cell 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.

投稿日時:
最終更新: