H - 都市の巡回調査 / City Tour Survey 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This is a problem where you simulate the order of visiting all cities on a directed graph following specific rules. At each tour, you greedily choose the “unvisited adjacent city with the highest importance,” and when there are no more destinations, you start a new tour.
Analysis
Problem Breakdown
Organizing the procedure, the following two operations alternate:
- Starting a tour: The first tour starts from city \(1\); subsequent tours start from the unvisited city with the highest importance.
- Moving during a tour: Among the unvisited cities reachable via edges from the current city, move to the one with the highest importance. If there is no such city, the tour ends.
Issues with the Naive Approach
If we naively search for “the unvisited adjacent city with the highest importance” at each step, we need to scan the entire adjacency list every time. In the worst case, we end up checking the same edges repeatedly, resulting in \(O(N \times M)\) time complexity with the risk of TLE.
Optimization Idea
Pre-sort each adjacency list in descending order of importance, and maintain a pointer (cursor) for each vertex. The pointer indicates “the next position to check,” and elements already determined to be visited are never revisited. This ensures the total scan across all adjacency lists is bounded by \(O(M)\).
Algorithm
Preprocessing: Sort each vertex’s adjacency list
adj[u]in descending order of the destination’s importance \(B[v]\). Also create a reverse lookup arrayimp_to_citymapping importance to city number.Managing tour starting cities: Insert all cities (except city \(1\)) into a max-heap of importance (in Python, a min-heap using negated values). When starting a new tour, pop from the heap, and if the city is unvisited, start the tour from that city.
Moving during a tour (pointer method):
- Prepare a pointer
ptr[u]for each vertex \(u\) (initialized to \(0\)). - Scan the adjacency list of the current city
currentstarting from positionptr[current]. - Skip visited cities and advance
ptr[current]. - If an unvisited city is found, move there and mark it as visited.
- If none is found, the tour ends.
- Prepare a pointer
Repeat until all cities are visited.
Concrete Example
Given \(4\) cities, edges \(1 \to 2, 1 \to 3, 2 \to 4\), and importance \(B = [3, 4, 1, 2]\) (city 1 has importance 3, city 2 has importance 4, …):
- 1st tour: Start from city \(1\) → adjacent unvisited cities are \(\{2, 3\}\), with importance \(B[2]=4, B[3]=1\), so move to city \(2\) → city \(2\)’s adjacent unvisited is \(\{4\}\), so move to city \(4\) → city \(4\) has no adjacent unvisited → tour ends. Visit order: \(1, 2, 4\)
- 2nd tour: The unvisited city with the highest importance is city \(3\) (importance \(1\)) → no adjacent unvisited → tour ends. Visit order: \(3\)
- Output:
1 2 4 3
Complexity
- Time complexity: \(O((N + M) \log N)\)
- Sorting adjacency lists: \(O(M \log N)\)
- Heap operations: \(O(N \log N)\)
- Adjacency list traversal via pointers: each edge is skipped at most once, so \(O(M)\) total
- Space complexity: \(O(N + M)\)
Implementation Notes
The pointer method is the key: By using
ptr[u], we avoid re-checking previously skipped visited vertices. Since the sorted list has higher importance toward the front, the first unvisited city found while advancing the pointer is directly the candidate with the highest importance.Max-heap workaround: Since Python’s
heapqis a min-heap, we insert with negated importance (-B[i]) to simulate a max-heap.The first tour is special: Note that the starting city is fixed as city \(1\) rather than “the unvisited city with the highest importance.” This is controlled by the
first_tourflag.Source Code
import sys
from heapq import heappush, heappop
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
B = [0] * (N + 1)
for i in range(1, N + 1):
B[i] = int(input_data[idx]); idx += 1
adj = [[] for _ in range(N + 1)]
for _ in range(M):
u = int(input_data[idx]); idx += 1
v = int(input_data[idx]); idx += 1
adj[u].append(v)
# For each city, sort neighbors by importance descending for efficient traversal
# We'll use a max-heap (negate values) for neighbors
visited = [False] * (N + 1)
result = []
# We need to efficiently find the unvisited city with max importance for new tours
# Use a max-heap of all cities by importance
# For the first tour, start city is 1
# For subsequent tours, pick unvisited city with max importance
# For adjacency, we need to pick unvisited neighbor with max importance
# We can use a heap per node, but that's expensive
# Instead, sort adjacency lists by importance descending, and use a heap during traversal
# Sort adjacency lists by B[v] descending
for u in range(N + 1):
adj[u].sort(key=lambda v: -B[v])
# Global heap for picking start cities (max-heap by importance)
# We'll add all cities except city 1
global_heap = []
for i in range(1, N + 1):
if i != 1:
heappush(global_heap, -B[i])
# Map from importance to city
imp_to_city = [0] * (N + 1)
for i in range(1, N + 1):
imp_to_city[B[i]] = i
# First tour starts at city 1
first_tour = True
start_city = 1
while True:
if first_tour:
current = start_city
first_tour = False
else:
# Find unvisited city with max importance
current = -1
while global_heap:
neg_imp = heappop(global_heap)
imp = -neg_imp
city = imp_to_city[imp]
if not visited[city]:
current = city
break
if current == -1:
break
if visited[current]:
# This shouldn't happen for first tour if city 1 is valid
# For subsequent tours, we already checked
continue
# Start tour from current
visited[current] = True
result.append(current)
# Use pointer-based approach for adjacency lists
# For each node, maintain a pointer into its sorted adjacency list
# But we need to skip visited nodes
while True:
# Find unvisited neighbor with max importance
found = False
best_neighbor = -1
best_imp = -1
for v in adj[current]:
if not visited[v]:
if B[v] > best_imp:
best_imp = B[v]
best_neighbor = v
# Since sorted descending, first unvisited is the best
found = True
break
if not found:
break
current = best_neighbor
visited[current] = True
result.append(current)
if len(result) == N:
break
sys.stdout.write(' '.join(map(str, result)) + '\n')
# The above has worst case O(N*M) due to scanning adjacency lists
# Let's optimize with pointers into sorted adjacency lists
def main_optimized():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
B = [0] * (N + 1)
for i in range(1, N + 1):
B[i] = int(input_data[idx]); idx += 1
adj = [[] for _ in range(N + 1)]
for _ in range(M):
u = int(input_data[idx]); idx += 1
v = int(input_data[idx]); idx += 1
adj[u].append(v)
for u in range(N + 1):
adj[u].sort(key=lambda v: -B[v])
imp_to_city = [0] * (N + 1)
for i in range(1, N + 1):
imp_to_city[B[i]] = i
visited = [False] * (N + 1)
ptr = [0] * (N + 1) # pointer into adj[u]
result = []
global_heap = []
for i in range(1, N + 1):
if i != 1:
heappush(global_heap, -B[i])
first_tour = True
while len(result) < N:
if first_tour:
current = 1
first_tour = False
else:
current = -1
while global_heap:
imp = -heappop(global_heap)
city = imp_to_city[imp]
if not visited[city]:
current = city
break
if current == -1:
break
if visited[current]:
continue
visited[current] = True
result.append(current)
while True:
nxt = -1
while ptr[current] < len(adj[current]):
v = adj[current][ptr[current]]
if not visited[v]:
nxt = v
break
ptr[current] += 1
if nxt == -1:
break
current = nxt
visited[current] = True
result.append(current)
sys.stdout.write(' '.join(map(str, result)) + '\n')
main_optimized()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: