E - 観光ツアーの最適ルート / Optimal Route for a Sightseeing Tour 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This is a problem on a graph consisting of \(N\) tourist spots, where you travel from start \(S\) to goal \(T\) and maximize “total satisfaction of visited tourist spots − total transportation cost of roads traversed.” The key point is that even if you visit the same spot multiple times, the satisfaction is only counted once.
Analysis
Difficulty of the Problem
This problem differs from a simple shortest path problem in the following two ways:
- Satisfaction for the same spot is only counted once → We need to track “which spots have been visited”
- Passing through the same spot multiple times is allowed → The route is not necessarily a simple path, and it may be beneficial to take a detour to visit a spot with high satisfaction
For example, we need to consider routes like \(S \to A \to B \to A \to T\), where we visit \(B\) and come back. If \(B\)’s satisfaction is high enough, the gain may increase even after paying the extra transportation cost.
Issues with a Naive Approach
Enumerating all routes causes an explosion in the number of paths since the same node can be visited multiple times. However, noting the constraint \(N \leq 12\), we can represent the “set of visited spots” as a bitmask (\(2^{12} = 4096\) possibilities).
Key Insight
If we represent the state as (current node, set of visited spots), then when the same state is reached more than once, we only need to keep the one with the smaller cost. This reduces the problem to a shortest path problem on an expanded graph.
Specifically, instead of maximizing profit = total satisfaction − total transportation cost, we transform it into minimizing total transportation cost − total satisfaction. Edge costs are positive (\(W_j \geq 1\)), and when visiting a new node, we subtract \(P_v\) (= gaining satisfaction). Since edge weights are non-negative, Dijkstra’s algorithm is applicable.
Algorithm
- State definition: \((u, \text{mask})\) represents “currently at node \(u\), with the set of spots visited so far being \(\text{mask}\)”
- Cost definition: \(\text{dist}[\text{mask}][u]\) = minimum value of “total transportation cost − total satisfaction” to reach that state
- Initial state: \(\text{dist}[1 \ll S][S] = -P_S\) (gaining the satisfaction of the starting point)
- Transitions: When traversing edge \((u, v, w)\) from node \(u\):
- If \(v\) is unvisited: new cost \(= d + w - P_v\), new mask \(= \text{mask} \mid (1 \ll v)\)
- If \(v\) is already visited: new cost \(= d + w\), new mask \(= \text{mask}\) (no additional satisfaction)
- Answer: Find the minimum of \(\text{dist}[\text{mask}][T]\) over all masks \(\text{mask}\) that contain both \(S\) and \(T\), and negate its sign
In Dijkstra’s algorithm, the transition cost \(w\) (edge weight) is positive, but the effective cost can become negative when \(-P_v\) is added upon visiting a new node. However, negative costs only occur when visiting a node for the first time, and each node is visited for the first time at most once, so Dijkstra’s algorithm works correctly (negative costs arise in transitions where the mask monotonically increases, and revisiting the same state always has non-negative cost).
Complexity
- Time complexity: \(O(2^N \cdot N \cdot (N + M) \cdot \log(2^N \cdot N))\)
- The number of states is \(2^N \times N\), adjacent edges are explored from each state, and priority queue operations cost \(\log\)
- Since \(N \leq 12\), there are about \(2^{12} \times 12 = 49152\) states, which is sufficiently fast
- Space complexity: \(O(2^N \cdot N)\) (for the distance table)
Implementation Notes
Bitmask handling: The visited status of node \(i\) is checked with
mask & (1 << i)and updated to visited withmask | (1 << i)Conversion to minimization: By converting the maximization of “satisfaction − cost” to the minimization of “cost − satisfaction,” Dijkstra’s algorithm can be naturally applied
Recovering the answer: Finally, negate the sign of the minimum value of \(\text{dist}[\text{mask}][T]\) and output it. As long as goal \(T\) is reached, it doesn’t matter which spots were visited along the way, so we search all masks where the bits for both \(S\) and \(T\) are set
Conversion to 0-indexed: The input is 1-indexed, but internally it is converted to 0-indexed for bitmask management
Source Code
import sys
from heapq import heappush, heappop
def main():
input_data = sys.stdin.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
P = [0] * N
for i in range(N):
P[i] = int(input_data[idx]); idx += 1
S = int(input_data[idx]) - 1; idx += 1
T = int(input_data[idx]) - 1; idx += 1
# Adjacency list
adj = [[] for _ in range(N)]
for _ in range(M):
u = int(input_data[idx]) - 1; idx += 1
v = int(input_data[idx]) - 1; idx += 1
w = int(input_data[idx]); idx += 1
adj[u].append((v, w))
adj[v].append((u, w))
# State: (current_node, visited_set_bitmask)
# We want to maximize: sum of P[i] for i in visited - sum of edge costs
# Equivalent to minimizing: sum of edge costs - sum of P[i] for i in visited
# Use Dijkstra-like approach on (node, bitmask) states
# cost[node][mask] = minimum (total edge cost - total satisfaction) to reach node with visited set = mask
INF = float('inf')
# dist[mask][node] = min cost (edge_cost - satisfaction)
# We negate satisfaction: when we visit a new node, we subtract P[i]
dist = [[INF] * N for _ in range(1 << N)]
start_mask = 1 << S
dist[start_mask][S] = -P[S] # We start at S, gain P[S], no edge cost yet
# Priority queue: (cost, node, mask)
pq = []
heappush(pq, (-P[S], S, start_mask))
while pq:
d, u, mask = heappop(pq)
if d > dist[mask][u]:
continue
for v, w in adj[u]:
new_mask = mask | (1 << v)
# If v was already visited, no new satisfaction
if mask & (1 << v):
new_cost = d + w
else:
new_cost = d + w - P[v]
if new_cost < dist[new_mask][v]:
dist[new_mask][v] = new_cost
heappush(pq, (new_cost, v, new_mask))
# Find minimum cost over all masks that include both S and T, ending at T
ans = INF
required = (1 << S) | (1 << T)
for mask in range(1 << N):
if (mask & required) == required:
if dist[mask][T] < ans:
ans = dist[mask][T]
# The profit is -ans (since we minimized cost - satisfaction)
print(-ans)
main()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: