Given a set of N cities and the distance between every pair of cities, find the shortest possible route that visits every city exactly once and returns to the starting city.
Input:
N =4dist =[[0,10,15,20],[10,0,35,25],[15,35,0,30],[20,25,30,0]]Output: 80Explanation: The minimum cost tour is1→2→4→3→1with cost 10+25+30+15=80.
Note the difference between Hamiltonian Cycle and TSP. The Hamiltonian cycle problem asks if there exists a tour that visits every city exactly once. In TSP, we know a Hamiltonian tour exists (since the graph is complete), and the goal is to find the minimum weight Hamiltonian cycle.
The problem is a classic NP-hard problem. There is no known polynomial time solution.
classSolution {
public:int tsp(vector<vector<int>>& cost) {
int n = cost.size(), ans = INT_MAX;
vector<int> cities(n-1);
iota(cities.begin(), cities.end(), 1);
do {
int cur = cost[0][cities[0]];
for (int i =0; i < n-2; ++i) cur += cost[cities[i]][cities[i+1]];
cur += cost[cities.back()][0];
ans = min(ans, cur);
} while (next_permutation(cities.begin(), cities.end()));
return ans;
}
};
classSolution {
publicinttsp(int[][] cost) {
int n = cost.length, ans = Integer.MAX_VALUE;
List<Integer> cities =new ArrayList<>();
for (int i = 1; i < n; ++i) cities.add(i);
List<List<Integer>> perms = permute(cities);
for (List<Integer> perm : perms) {
int cur = cost[0][perm.get(0)];
for (int i = 0; i < n-2; ++i) cur += cost[perm.get(i)][perm.get(i+1)];
cur += cost[perm.get(n-2)][0];
ans = Math.min(ans, cur);
}
return ans;
}
private List<List<Integer>>permute(List<Integer> nums) {
List<List<Integer>> res =new ArrayList<>();
permuteHelper(nums, 0, res);
return res;
}
privatevoidpermuteHelper(List<Integer> nums, int start, List<List<Integer>> res) {
if (start == nums.size()) { res.add(new ArrayList<>(nums)); return; }
for (int i = start; i < nums.size(); ++i) {
Collections.swap(nums, i, start);
permuteHelper(nums, start+1, res);
Collections.swap(nums, i, start);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
classSolution:
deftsp(self, cost: list[list[int]]) -> int:
from itertools import permutations
n = len(cost)
ans = float('inf')
for perm in permutations(range(1, n)):
cur = cost[0][perm[0]]
for i in range(n-2):
cur += cost[perm[i]][perm[i+1]]
cur += cost[perm[-1]][0]
ans = min(ans, cur)
return ans
The idea is to compute the minimum-cost path for every subset of cities and every possible endpoint using dynamic programming. Fix city 0 as the start. For any subset S that includes city 0 and any city i in S (i != 0), let C(S, i) be the minimum cost to start at 0, visit every city in S exactly once, and end at i. If we can compute C(S, i) for all S and i, the answer is min_i C(All, i) + dist[i][0]. This is the classic Held–Karp algorithm.
This works because any optimal path to (S, i) must come from some j in S \ {i} by taking an optimal path to (S \ {i}, j) and adding edge (j, i). We reuse previously computed optimal subpaths to build larger ones.
Number the cities 0..n-1 and fix 0 as the start/finish city.
Use a DP table dp[mask][i], where mask is a bitmask representing a subset S (mask includes bit 0), and i is the last city in S (i != 0). dp[mask][i] stores C(S, i).
Base case: for every city i != 0, dp[(1<<0)|(1<<i)][i] = dist[0][i]. This represents visiting only {0,i} and ending at i.
Transition: for increasing sizes of mask, for every mask and every i in mask (i != 0):
classSolution {
public:int tsp(vector<vector<int>>& dist) {
int n = dist.size();
if (n ==1) return0;
int ALL = (1<< n) -1;
constint INF =1e9;
vector<vector<int>> dp(1<<n, vector<int>(n, INF));
// base cases
for (int i =1; i < n; ++i) dp[(1<<0)|(1<<i)][i] = dist[0][i];
for (int mask =0; mask <= ALL; ++mask) {
if (!(mask &1)) continue; // must include start(0)
for (int i =1; i < n; ++i) {
if (!(mask & (1<<i))) continue;
int prevMask = mask ^ (1<<i);
for (int j =1; j < n; ++j) {
if (!(prevMask & (1<<j))) continue;
dp[mask][i] = min(dp[mask][i], dp[prevMask][j] + dist[j][i]);
}
}
}
int ans = INF;
for (int i =1; i < n; ++i) ans = min(ans, dp[ALL][i] + dist[i][0]);
return ans;
}
};
classSolution:
deftsp(self, dist: list[list[int]]) -> int:
n = len(dist)
if n ==1:
return0 ALL = (1<< n) -1 INF =10**9 dp = [[INF] * n for _ in range(1<<n)]
for i in range(1, n):
dp[(1<<0)|(1<<i)][i] = dist[0][i]
for mask in range(1<<n):
if (mask &1) ==0:
continuefor i in range(1, n):
if (mask & (1<<i)) ==0:
continue prev = mask ^ (1<<i)
for j in range(1, n):
if (prev & (1<<j)) ==0:
continue dp[mask][i] = min(dp[mask][i], dp[prev][j] + dist[j][i])
ans = min(dp[ALL][i] + dist[i][0] for i in range(1, n))
return ans
⏰ Time complexity: O(n^2 * 2^n), because there are 2^n masks and for each mask and endpoint we iterate over up to n previous endpoints (and another factor n when enumerating endpoints), resulting in n^2 * 2^n work.
🧺 Space complexity: O(n * 2^n), for the DP table dp[mask][i] storing 2^n * n states.
Require the distance matrix dist to satisfy the triangle inequality. If distances are missing, treat them as INF and abort if MST is not spanning.
Compute an MST of the n vertices (Prim’s or Kruskal). Use vertex 0 as the root.
Perform a preorder traversal (DFS) of the MST, record the visitation order order (each vertex appears once when first visited).
Form the tour by visiting vertices in order and then returning to 0. Compute the total cost by summing dist[order[i]][order[i+1]] and finally dist[order.back()][0].
Return the tour cost and the tour (optional). The triangle inequality guarantees that replacing repeated visits in the full Euler walk by direct edges does not increase cost.
Edge cases and checks:
If n == 1 return cost 0 and tour [0].
If MST is not spanning (some vertex unreachable) return INF/indicate impossibility.
Use long long/large sentinel when summing weights to avoid overflow.
classSolution {
public:// dist: n x n matrix (complete graph). Returns tour cost (approx) or large INF if impossible.
longlong tsp_approx(vector<vector<int>>& dist) {
int n = dist.size();
if (n ==1) return0;
constlonglong INF =9e18;
// Prim's algorithm (dense version) to build MST parent array
vector<int> parent(n, -1);
vector<longlong> key(n, INF);
vector<char> inMST(n, 0);
key[0] =0; parent[0] =-1;
for (int count =0; count < n; ++count) {
int u =-1;
longlong best = INF;
for (int v =0; v < n; ++v) if (!inMST[v] && key[v] < best) { best = key[v]; u = v; }
if (u ==-1) return INF; // disconnected
inMST[u] =1;
for (int v =0; v < n; ++v) if (!inMST[v]) {
longlong w = dist[u][v];
if (w < key[v]) { key[v] = w; parent[v] = u; }
}
}
// build adjacency list for MST
vector<vector<int>> adj(n);
for (int v =1; v < n; ++v) {
int p = parent[v];
if (p >=0) { adj[p].push_back(v); adj[v].push_back(p); }
}
// preorder traversal (stack iterative)
vector<int> order; order.reserve(n);
vector<int> st; st.push_back(0);
vector<int> it(n, 0);
vector<char> vis(n, 0);
while (!st.empty()) {
int u = st.back(); st.pop_back();
if (vis[u]) continue;
vis[u] =1; order.push_back(u);
// push neighbors in reverse to get a deterministic preorder
for (int i = (int)adj[u].size()-1; i >=0; --i) {
int v = adj[u][i]; if (!vis[v]) st.push_back(v);
}
}
if ((int)order.size() != n) return INF; // not spanning
longlong ans =0;
for (int i =0; i < n-1; ++i) ans += dist[order[i]][order[i+1]];
ans += dist[order.back()][0];
return ans;
}
};
classSolution {
publiclongtsp_approx(int[][] dist) {
int n = dist.length;
if (n == 1) return 0;
finallong INF = Long.MAX_VALUE/ 4;
long[] key =newlong[n]; Arrays.fill(key, INF);
int[] parent =newint[n]; Arrays.fill(parent, -1);
boolean[] inMST =newboolean[n];
key[0]= 0;
for (int k = 0; k < n; ++k) {
int u =-1; long best = INF;
for (int i = 0; i < n; ++i) if (!inMST[i]&& key[i]< best) { best = key[i]; u = i; }
if (u ==-1) return INF;
inMST[u]=true;
for (int v = 0; v < n; ++v) if (!inMST[v]) {
long w = dist[u][v]; if (w < key[v]) { key[v]= w; parent[v]= u; }
}
}
List<Integer>[] adj =new List[n]; for (int i = 0; i < n; ++i) adj[i]=new ArrayList<>();
for (int v = 1; v < n; ++v) { int p = parent[v]; if (p >= 0) { adj[p].add(v); adj[v].add(p); } }
// iterative preorder List<Integer> order =new ArrayList<>();
Deque<Integer> st =new ArrayDeque<>(); st.push(0);
boolean[] vis =newboolean[n];
while (!st.isEmpty()) {
int u = st.pop(); if (vis[u]) continue; vis[u]=true; order.add(u);
List<Integer> nbrs = adj[u]; for (int i = nbrs.size()-1; i >= 0; --i) { int v = nbrs.get(i); if (!vis[v]) st.push(v); }
}
if (order.size() != n) return INF;
long ans = 0;
for (int i = 0; i < n-1; ++i) ans += dist[order.get(i)][order.get(i+1)];
ans += dist[order.get(n-1)][0];
return ans;
}
}
classSolution:
deftsp_approx(self, dist: list[list[int]]) -> int:
n = len(dist)
if n ==1:
return0 INF =10**18 key = [INF]*n
parent = [-1]*n
inMST = [False]*n
key[0] =0for _ in range(n):
u =-1 best = INF
for i in range(n):
ifnot inMST[i] and key[i] < best:
best = key[i]; u = i
if u ==-1:
return INF
inMST[u] =Truefor v in range(n):
ifnot inMST[v] and dist[u][v] < key[v]:
key[v] = dist[u][v]; parent[v] = u
adj = [[] for _ in range(n)]
for v in range(1, n):
p = parent[v]
if p >=0:
adj[p].append(v); adj[v].append(p)
order = []
st = [0]
vis = [False]*n
while st:
u = st.pop()
if vis[u]:
continue vis[u] =True order.append(u)
for v in reversed(adj[u]):
ifnot vis[v]: st.append(v)
if len(order) != n:
return INF
ans =0for i in range(n-1): ans += dist[order[i]][order[i+1]]
ans += dist[order[-1]][0]
return ans
⏰ Time complexity: O(n^2), because Prim’s dense implementation takes O(n^2) on an n x n distance matrix and DFS/preorder is O(n) on the MST; overall dominated by O(n^2).
🧺 Space complexity: O(n^2) if the input distance matrix is counted, otherwise additional auxiliary space is O(n) for MST structures and traversal (parents, adjacency, stack).
This method requires the triangle inequality to guarantee the 2-approximation factor. If triangle inequality does not hold, the shortcutting step can increase cost.
Christofides’ algorithm improves the guarantee to 1.5 for metric TSP (triangle inequality) but is more complex to implement (requires minimum-weight perfect matching on odd-degree vertices). See: http://en.wikipedia.org/wiki/Christofides_algorithm