E - 宅配ドライバーの巡回 / Delivery Driver's Route 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This problem asks for the shortest travel time when starting from the office, visiting all \(K\) delivery destinations, and returning to the office. Given the constraint \(K \leq 15\), we solve the Traveling Salesman Problem (TSP) using bitmask DP.
Analysis
Problems with the Naive Approach
There are \(K!\) possible orderings for visiting \(K\) delivery destinations. When \(K = 15\), \(15! \approx 1.3 \times 10^{12}\), so trying all permutations is far too slow.
Key Observations
Shortest paths on the graph can be precomputed: When considering the tour order, we don’t need to traverse intersections one by one. As long as we know the shortest distances between the office \(S\) and each delivery destination \(D_j\), the problem reduces to “TSP over \((K+1)\) vertices.”
\(K \leq 15\) is a signal for bitmask DP: Although the Traveling Salesman Problem is NP-hard, when the number of vertices is small (roughly \(20\) or fewer), it can be solved with bitmask DP in \(O(2^K \cdot K^2)\).
Solution Outline
- Run Dijkstra’s algorithm from each of the \((K+1)\) vertices \(S, D_1, D_2, \ldots, D_K\) to build a shortest distance table \(\mathrm{cost}[i][j]\) between all important vertices.
- Use bitmask DP to find the minimum cost of starting from \(S\), visiting all delivery destinations, and returning to \(S\).
Algorithm
Step 1: Building the Shortest Distance Table
Run Dijkstra’s algorithm from each of the \((K+1)\) vertices \(S, D_1, D_2, \ldots, D_K\). This gives us the shortest distance \(\mathrm{cost}[i][j]\) between any two important vertices.
For example, if \(K=3\), \(S=1\), and the delivery destinations are \(\{3, 5, 7\}\), we run Dijkstra from vertices \(1, 3, 5, 7\) and build a \(4 \times 4\) distance table.
Step 2: Bitmask DP (Traveling Salesman Problem)
- State: \(\mathrm{dp}[\mathrm{mask}][i]\) = the minimum travel cost from \(S\) when the set of visited delivery destinations is \(\mathrm{mask}\) (managed with bits) and we are currently at delivery destination \(i\)
- Initial state: \(\mathrm{dp}[1 \ll j][j+1] = \mathrm{cost}[0][j+1]\) (going directly from \(S\) to delivery destination \(j\))
- Transition: When at vertex \(u\) with visited set \(\mathrm{mask}\), move to an unvisited vertex \(v\) $\(\mathrm{dp}[\mathrm{mask} \mid (1 \ll (v-1))][v] = \min\left(\mathrm{dp}[\mathrm{mask} \mid (1 \ll (v-1))][v],\; \mathrm{dp}[\mathrm{mask}][u] + \mathrm{cost}[u][v]\right)\)$
- Answer: The minimum cost of returning to \(S\) from the state \(\mathrm{full\_mask} = 2^K - 1\) where all delivery destinations have been visited $\(\mathrm{ans} = \min_{u=1}^{K}\left(\mathrm{dp}[\mathrm{full\_mask}][u] + \mathrm{cost}[u][0]\right)\)$
Complexity
- Time complexity: \(O((K+1) \cdot (N + M) \log N + 2^K \cdot K^2)\)
- Running Dijkstra’s algorithm \((K+1)\) times: \(O((K+1)(N + M)\log N)\)
- Bitmask DP: \(O(2^K \cdot K^2)\)
- Since \(K \leq 15\), \(2^{15} \cdot 15^2 \approx 7.4 \times 10^6\), which is sufficiently fast
- Space complexity: \(O(2^K \cdot K + (K+1) \cdot N)\)
Implementation Notes
Node index mapping: Manage as
nodes[0] = S,nodes[1..K] = D[0..K-1], and use the bitmask only for delivery destinations (\(K\) bits). \(S\) is not included in the bitmask.Dijkstra source vertices: Dijkstra must be run from both \(S\) and all delivery destinations. Running only from \(S\) would not give us the shortest distances between pairs of delivery destinations (although the graph is bidirectional so distances from \(S\) to each destination are known, distances between destinations require separate computation).
Bit correspondence: Delivery destination \(D_j\) (
nodes[j+1]) corresponds to bit \(j\) of \(\mathrm{mask}\). Usemask & (1 << (u-1))to check whether vertex \(u\) (1-indexed in nodes) has been visited.Source Code
import heapq
import sys
def dijkstra(graph, start, N):
dist = [float('inf')] * (N + 1)
dist[start] = 0
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
for v, w in graph[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(pq, (nd, v))
return dist
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
graph = [[] for _ in range(N + 1)]
for _ in range(M):
u = int(input_data[idx]); idx += 1
v = int(input_data[idx]); idx += 1
w = int(input_data[idx]); idx += 1
graph[u].append((v, w))
graph[v].append((u, w))
S = int(input_data[idx]); idx += 1
K = int(input_data[idx]); idx += 1
D = []
for _ in range(K):
D.append(int(input_data[idx])); idx += 1
# nodes[0] = S, nodes[1..K] = D[0..K-1]
nodes = [S] + D
# Dijkstra from each important node
dist_from = {}
for node in nodes:
dist_from[node] = dijkstra(graph, node, N)
# cost[i][j] = shortest distance between nodes[i] and nodes[j]
num = K + 1 # 0 = S, 1..K = delivery points
cost = [[0] * num for _ in range(num)]
for i in range(num):
for j in range(num):
cost[i][j] = dist_from[nodes[i]][nodes[j]]
# TSP with bitmask DP
# State: (mask, i) where mask is subset of {0, 1, ..., K-1} representing visited delivery points
# i is current position (0 = S, 1..K = delivery points)
# We need to visit all K delivery points and return to S
full_mask = (1 << K) - 1
INF = float('inf')
# dp[mask][i] = min cost to have visited delivery points in mask, currently at delivery point i (1-indexed in nodes)
dp = [[INF] * (K + 1) for _ in range(1 << K)]
# Start from S (node index 0), go to each delivery point
for j in range(K):
dp[1 << j][j + 1] = cost[0][j + 1]
for mask in range(1, 1 << K):
for u in range(1, K + 1):
if dp[mask][u] == INF:
continue
if not (mask & (1 << (u - 1))):
continue
for v in range(1, K + 1):
if mask & (1 << (v - 1)):
continue
new_mask = mask | (1 << (v - 1))
nd = dp[mask][u] + cost[u][v]
if nd < dp[new_mask][v]:
dp[new_mask][v] = nd
# Find minimum: visit all, then return to S
ans = INF
for u in range(1, K + 1):
val = dp[full_mask][u] + cost[u][0]
if val < ans:
ans = val
print(ans)
if __name__ == '__main__':
main()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: