1
- """
2
- Given a list of cities and the distances between every pair of cities, the Travelling Salesman Problem (TSP) is to
3
- find the shortest possible route that visits every city exactly once and returns to the starting city.
4
-
5
- This problem can be solved using the concept of "DYNAMIC PROGRAMMING".
6
-
7
- We use a bitmask to represent which cities have been visited and calculate the minimum cost to complete the tour.
8
-
9
- Example - distances = [
10
- [0, 10, 15, 20],
11
- [10, 0, 35, 25],
12
- [15, 35, 0, 30],
13
- [20, 25, 30, 0]
14
- ]
15
- Output: 80
16
- """
1
+ #!/usr/bin/env python3
17
2
18
3
from functools import lru_cache
19
4
20
5
21
6
def tsp (distances : list [list [int ]]) -> int :
22
7
"""
23
- The tsp function solves the Travelling Salesman Problem (TSP) using dynamic programming and bitmasking.
24
- It calculates the minimum cost to visit all cities and return to the starting city.
8
+ Solves the Travelling Salesman Problem (TSP) using dynamic programming and bitmasking.
9
+
10
+ Args:
11
+ distances: A 2D list where distances[i][j] represents the distance between city i and city j.
12
+
13
+ Returns:
14
+ The minimum cost to complete the tour visiting all cities.
15
+
16
+ Raises:
17
+ ValueError: If any distance is negative.
25
18
26
19
>>> tsp([[0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]])
27
20
80
28
21
>>> tsp([[0, 29, 20, 21], [29, 0, 15, 17], [20, 15, 0, 28], [21, 17, 28, 0]])
29
22
69
30
- >>> tsp([[0, 10, -15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]])
23
+ >>> tsp([[0, 10, -15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]]) # doctest: +ELLIPSIS
31
24
Traceback (most recent call last):
32
25
...
33
26
ValueError: Distance cannot be negative
@@ -36,35 +29,26 @@ def tsp(distances: list[list[int]]) -> int:
36
29
if any (distances [i ][j ] < 0 for i in range (n ) for j in range (n )):
37
30
raise ValueError ("Distance cannot be negative" )
38
31
39
- VISITED_ALL = (1 << n ) - 1
32
+ visited_all = (1 << n ) - 1
40
33
41
34
@lru_cache (None )
42
35
def visit (city : int , mask : int ) -> int :
43
- """
44
- Recursively calculate the minimum cost of visiting all cities, starting at 'city' with visited cities encoded in 'mask'.
45
- """
46
- if mask == VISITED_ALL :
47
- return distances [city ][0 ] # Return to the starting city
36
+ """Recursively calculates the minimum cost of visiting all cities."""
37
+ if mask == visited_all :
38
+ return distances [city ][0 ] # Return to start
48
39
49
- min_cost = float (" inf" )
40
+ min_cost = float (' inf' )
50
41
for next_city in range (n ):
51
- if not mask & (1 << next_city ): # If the next_city is not visited
42
+ if not mask & (1 << next_city ): # If next_city is unvisited
52
43
new_cost = distances [city ][next_city ] + visit (
53
44
next_city , mask | (1 << next_city )
54
45
)
55
46
min_cost = min (min_cost , new_cost )
56
47
return min_cost
57
48
58
- return visit (0 , 1 ) # Start at city 0 with only city 0 visited
49
+ return visit (0 , 1 ) # Start from city 0 with only city 0 visited
59
50
60
51
61
52
if __name__ == "__main__" :
62
53
import doctest
63
-
64
54
doctest .testmod ()
65
- print (
66
- f"{ tsp ([[0 , 10 , 15 , 20 ], [10 , 0 , 35 , 25 ], [15 , 35 , 0 , 30 ], [20 , 25 , 30 , 0 ]]) = } "
67
- )
68
- print (
69
- f"{ tsp ([[0 , 29 , 20 , 21 ], [29 , 0 , 15 , 17 ], [20 , 15 , 0 , 28 ], [21 , 17 , 28 , 0 ]]) = } "
70
- )
0 commit comments