Official

E - 配送ルートの最適化 / Optimization of Delivery Routes Editorial by admin

Claude 4.6 Opus (Thinking)

Overview

This is the so-called Traveling Salesman Problem (TSP): starting from the distribution center (destination \(1\)), visit all \(N\) destinations exactly once, and return to the distribution center, minimizing the fuel cost (the sum of squared Euclidean distances).

Analysis

Essence of the Problem

This problem asks us to find, on a complete graph with \(N\) vertices, the Hamiltonian cycle starting and ending at vertex \(1\) (a cycle that visits every vertex exactly once) with the minimum total edge weight.

Naive Approach and Its Limitations

The simplest method is to enumerate all permutations of the \(N\) destinations. However, the number of permutations is \((N-1)!\), and when \(N = 16\), this gives \((16-1)! = 15! \approx 1.3 \times 10^{12}\), which is far too large to handle in time.

How to Solve It

While enumerating all visit orders causes a state explosion, if we only record “which destinations have been visited (as a set)” and “which destination we are currently at”, the detailed order of intermediate visits becomes unnecessary. This is the idea behind bitmask DP (bit DP).

Since \(N \leq 16\), the visited set can be represented as an \(N\)-bit integer, and the number of states is \(2^N \times N\), which is at most \(2^{16} \times 16 = 1{,}048{,}576\) — small enough.

Algorithm

1. Precomputation of the Cost Matrix

For all destination pairs \((i, j)\), precompute the travel cost \((X_i - X_j)^2 + (Y_i - Y_j)^2\).

2. Bitmask DP Definition

  • State: \(dp[S][u]\) = “the minimum cost when the set of visited destinations is \(S\) (bitmask) and the last visited destination is \(u\)
  • Initial state: \(dp[\{0\}][0] = 0\) (we are at destination \(1\), i.e., vertex \(0\) in 0-indexed, with cost \(0\))
  • Transition: From a state where we are in set \(S\) at vertex \(u\), move to an unvisited vertex \(v\) (\(v \notin S\)): $\(dp[S \cup \{v\}][v] = \min(dp[S \cup \{v\}][v],\; dp[S][u] + \text{dist}[u][v])\)$

3. Computing the Answer

From the state where all vertices have been visited (\(S = \{0, 1, \ldots, N-1\}\), i.e., bitmask \(2^N - 1\)), add the cost of returning to the distribution center (vertex \(0\)) and take the minimum: $\(\text{ans} = \min_{u=0}^{N-1} \left( dp[2^N - 1][u] + \text{dist}[u][0] \right)\)$

Concrete Example

For \(N = 3\) with vertices \(0, 1, 2\): - \(dp[001][0] = 0\) (only vertex 0 visited, currently at vertex 0) - \(dp[011][1] = \text{dist}[0][1]\) (moved from vertex 0 → 1) - \(dp[111][2] = \text{dist}[0][1] + \text{dist}[1][2]\) (moved from vertex 0 → 1 → 2) - Finally, \(dp[111][2] + \text{dist}[2][0]\) gives the cost of returning to vertex 0

Complexity

  • Time complexity: \(O(2^N \times N^2)\) (for each of the \(2^N \times N\) states, we try \(N\) possible transitions)
    • When \(N = 16\), this is approximately \(2^{16} \times 16^2 \approx 1.7 \times 10^7\), which is fast enough
  • Space complexity: \(O(2^N \times N)\) (size of the DP table)

Implementation Notes

  • Destinations are given in 1-indexed form, but in the code they are handled as 0-indexed, with the distribution center as vertex \(0\).

  • States in bitmask \(S\) that do not include vertex \(0\) (the distribution center) are skipped to avoid unnecessary computation (if not (S & 1): continue).

  • The DP initial values are set to INF (infinity), naturally excluding unreachable states.

  • By precomputing the cost matrix, distance calculations during the DP are done in \(O(1)\).

    Source Code

import sys

def main():
    input_data = sys.stdin.read().split()
    idx = 0
    N = int(input_data[idx]); idx += 1
    coords = []
    for i in range(N):
        x = int(input_data[idx]); idx += 1
        y = int(input_data[idx]); idx += 1
        coords.append((x, y))
    
    # Precompute distance (cost) matrix
    dist = [[0] * N for _ in range(N)]
    for i in range(N):
        for j in range(N):
            dx = coords[i][0] - coords[j][0]
            dy = coords[i][1] - coords[j][1]
            dist[i][j] = dx * dx + dy * dy
    
    # TSP with bitmask DP
    # dp[S][i] = minimum cost to visit exactly the set S of nodes, ending at node i
    # Start at node 0 (1-indexed node 1)
    INF = float('inf')
    full = 1 << N
    dp = [[INF] * N for _ in range(full)]
    dp[1][0] = 0  # Start at node 0, visited set = {0}
    
    for S in range(1, full):
        if not (S & 1):
            continue  # Must include node 0
        for u in range(N):
            if dp[S][u] == INF:
                continue
            if not (S >> u & 1):
                continue
            for v in range(N):
                if S >> v & 1:
                    continue
                nS = S | (1 << v)
                cost = dp[S][u] + dist[u][v]
                if cost < dp[nS][v]:
                    dp[nS][v] = cost
    
    # Answer: visit all nodes and return to node 0
    full_mask = full - 1
    ans = INF
    for u in range(N):
        if dp[full_mask][u] + dist[u][0] < ans:
            ans = dp[full_mask][u] + dist[u][0]
    
    print(ans)

main()

This editorial was generated by claude4.6opus-thinking.

posted:
last update: