E - 冒険者の旅路 / The Adventurer's Journey 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This problem asks us to maximize our stamina upon arriving at town \(N\) when traveling from town \(1\) to town \(N\), utilizing inns at each town (which restore stamina only on the first visit). Given the small constraint \(N \leq 10\), we solve this using state-space search where we manage “which towns have been visited” with a bitmask.
Analysis
Key Observations
Inn recovery is only once per town: No matter how many times you visit the same town, recovery only happens on the first visit. Therefore, tracking “which towns have already been visited” is essential for calculating stamina.
You can traverse the same roads and towns any number of times: Unlike standard shortest path problems, it may be advantageous to take detours to visit more inns and recover stamina.
Why a naive shortest path approach fails: Standard Dijkstra’s algorithm finds the “minimum cost,” but in this problem, due to recovery amounts, the goal is to “maximize stamina.” Furthermore, since your stamina at the same town can differ depending on the set of previously visited towns, simply tracking “maximum stamina per town” is insufficient.
Example: If you arrive at town \(B\) via town \(A\) versus arriving at town \(B\) via town \(C\), the inns available for future visits differ, so even if your current stamina is lower, it may be more advantageous in the long run.
- The constraint \(N \leq 10\): Representing the visited set as a bitmask gives \(2^{10} = 1024\) possibilities. The total number of states is \(N \times 2^N \leq 10 \times 1024 = 10240\), which is sufficiently small.
State Definition
We define the state as (current town, bitmask of visited set) and track the maximum stamina achievable at each state.
Algorithm
We use a bitmask-based maximum stamina search (maximization variant of Dijkstra’s algorithm).
Initial state: Town \(0\) (town \(1\) in 0-indexed), visited mask \(= \{0\}\) (bit \(0\) is set), stamina \(= F + R_1\).
Priority queue: To process states with higher stamina first, we negate the stamina value and use a min-heap as a max-heap.
Transitions: When moving from current town \(u\) (stamina \(s\), visited mask \(mask\)) to adjacent town \(v\) with edge cost \(w\):
- Movement is impossible if \(s < w\)
- New stamina \(s' = s - w\)
- If town \(v\) is unvisited, then \(s' \mathrel{+}= R_v\) and the mask is updated
- If \(s'\) is greater than
best[v][new_mask], update and add to the queue
Answer: The maximum stamina among all states that reached town \(N-1\). If unreachable, output \(-1\).
Complexity
Number of states: \(N \times 2^N\)
Transitions from each state: at most \(N-1\) edges
Priority queue operations: \(O(\log(N \cdot 2^N))\) per state
Time complexity: \(O(N^2 \cdot 2^N \cdot \log(N \cdot 2^N))\)
- For \(N = 10\), this is approximately \(10^5\) states, which is sufficiently fast
Space complexity: \(O(N \cdot 2^N)\) (for the
besttable and priority queue)
Implementation Notes
Implementing a max-heap: Since Python’s
heapqis a min-heap, we negate the stamina values when inserting into the queue to achieve max-stamina-first search.Pruning: If the stamina of a state popped from the queue is less than the value in the
besttable, we skip it. This significantly reduces unnecessary exploration.Continue searching even after reaching town \(N\): It is important not to terminate the search upon reaching town \(N\), because it may be possible to pass through town \(N\), visit other inns, and return to town \(N\) with higher stamina.
Conversion to 0-indexed: The input is 1-indexed, but we convert to 0-indexed for easier bitmask management.
Source Code
import heapq
from itertools import combinations
def solve():
N, M, F = map(int, input().split())
R = list(map(int, input().split()))
adj = [[] for _ in range(N)]
for _ in range(M):
u, v, w = map(int, input().split())
u -= 1
v -= 1
adj[u].append((v, w))
adj[v].append((u, w))
# State: (current_town, frozenset of visited towns)
# We want to maximize stamina at town N-1
# N <= 10, so visited set can be bitmask (up to 2^10 = 1024)
# We use a modified Dijkstra-like approach but maximizing stamina
# State: (town, visited_mask)
# For each state, track maximum stamina achievable
# Use priority queue with negative stamina (max-heap via min-heap)
initial_stamina = F + R[0]
initial_mask = 1 << 0 # visited town 0
# best[town][mask] = max stamina known
best = [[- 1] * (1 << N) for _ in range(N)]
best[0][initial_mask] = initial_stamina
# Max-heap: (-stamina, town, mask)
pq = [(-initial_stamina, 0, initial_mask)]
ans = -1
while pq:
neg_stam, u, mask = heapq.heappop(pq)
stamina = -neg_stam
# If we already found a better way to this state, skip
if stamina < best[u][mask]:
continue
if u == N - 1:
ans = max(ans, stamina)
# Don't stop - might find better through different visited sets
# But we can continue exploring from town N-1 too
for v, w in adj[u]:
if stamina < w:
continue
new_stam = stamina - w
new_mask = mask
if not (mask & (1 << v)):
# First visit to v
new_stam += R[v]
new_mask = mask | (1 << v)
if new_stam > best[v][new_mask]:
best[v][new_mask] = new_stam
heapq.heappush(pq, (-new_stam, v, new_mask))
print(ans)
solve()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: