|
1 | 1 | #!/usr/bin/env python3
|
2 | 2 |
|
3 |
| - |
4 | 3 | def tsp(distances: list[list[int]]) -> int:
|
5 | 4 | """
|
6 | 5 | Solves the Travelling Salesman Problem (TSP)
|
7 | 6 | using dynamic programming and bitmasking.
|
| 7 | +
|
8 | 8 | Args:
|
9 | 9 | distances: 2D list where distances[i][j]
|
10 | 10 | is the distance between city i and city j.
|
11 | 11 |
|
12 | 12 | Returns:
|
13 |
| - Minimum cost to complete the tour |
14 |
| - visiting all cities. |
| 13 | + Minimum cost to complete the tour visiting all cities. |
| 14 | +
|
15 | 15 | Raises:
|
16 | 16 | ValueError: If any distance is negative.
|
17 |
| - >>> tsp([[0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]]) |
| 17 | +
|
| 18 | +>>> tsp([[0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]]) |
18 | 19 | 80
|
19 |
| - >>> tsp([[0, 29, 20, 21], [29, 0, 15, 17], [20, 15, 0, 28], [21, 17, 28, 0]]) |
| 20 | +>>> tsp([[0, 29, 20, 21], [29, 0, 15, 17], [20, 15, 0, 28], [21, 17, 28, 0]]) |
20 | 21 | 69
|
21 |
| - >>> tsp([[0, 10, -15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]]) |
22 |
| -
|
| 22 | +>>> tsp([[0, 10, -15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]]) |
| 23 | +# doctest: +ELLIPSIS |
| 24 | + Traceback (most recent call last): |
| 25 | + ... |
23 | 26 | ValueError: Distance cannot be negative
|
24 | 27 | """
|
25 | 28 | n = len(distances)
|
26 | 29 | if any(distances[i][j] < 0 for i in range(n) for j in range(n)):
|
27 | 30 | raise ValueError("Distance cannot be negative")
|
28 | 31 |
|
29 |
| - # Create a memoization table |
| 32 | + # Memoization table |
30 | 33 | memo = [[-1] * (1 << n) for _ in range(n)]
|
31 |
| - visited_all = (1 << n) - 1 |
| 34 | + visited_all = (1 << n) - 1 # All cities visited mask |
32 | 35 |
|
33 | 36 | def visit(city: int, mask: int) -> int:
|
34 | 37 | """Recursively calculates the minimum cost to visit all cities."""
|
35 | 38 | if mask == visited_all:
|
36 |
| - return distances[city][0] # Return to start |
| 39 | + return distances[city][0] # Return to the starting city |
37 | 40 | if memo[city][mask] != -1: # Return cached result if exists
|
38 | 41 | return memo[city][mask]
|
39 |
| - min_cost = 10**9 # Use a large integer instead of float('inf') |
| 42 | + |
| 43 | + min_cost = float('inf') # Use infinity for initial comparison |
40 | 44 | for next_city in range(n):
|
41 |
| - if not mask & (1 << next_city): # If unvisited |
| 45 | + if not (mask & (1 << next_city)): # If unvisited |
42 | 46 | new_cost = distances[city][next_city] + visit(
|
43 | 47 | next_city, mask | (1 << next_city)
|
44 | 48 | )
|
45 | 49 | min_cost = min(min_cost, new_cost)
|
46 | 50 | memo[city][mask] = min_cost # Store result in the memoization table
|
47 | 51 | return min_cost
|
48 |
| - |
49 |
| - return visit(0, 1) # Start from city 0 with city 0 visited |
50 |
| - |
| 52 | + return visit(0, 1) # Start from city 0 with only city 0 visited |
51 | 53 |
|
52 | 54 | if __name__ == "__main__":
|
53 | 55 | import doctest
|
54 |
| - |
55 | 56 | doctest.testmod()
|
0 commit comments