🧠
Top-Down DP with Memoization (Bitmask DP)
💡 Memoization caches results of subproblems to avoid recomputation, drastically improving efficiency over brute force.
Intuition
Store the minimum cost for each city and visited subset so that repeated states are computed once.
Algorithm
- Initialize a DP table to store results for each city and visited subset.
- Recursively compute the minimum cost for each state, returning cached results if available.
- Try all unvisited cities from the current city and update the DP table.
- Return the minimum cost starting from city 0 with only city 0 visited.
💡 Memoization avoids repeated work by caching results, making the algorithm feasible for n up to ~20.
Recurrence:dp[i][mask] = min_{j not in mask} (dist[i][j] + dp[j][mask | (1 << j)])
def tsp_memo(dist):
n = len(dist)
ALL_VISITED = (1 << n) - 1
memo = [[-1] * (1 << n) for _ in range(n)]
def dfs(i, mask):
if mask == ALL_VISITED:
return dist[i][0]
if memo[i][mask] != -1:
return memo[i][mask]
ans = float('inf')
for j in range(n):
if mask & (1 << j) == 0:
cost = dist[i][j] + dfs(j, mask | (1 << j))
if cost < ans:
ans = cost
memo[i][mask] = ans
return ans
return dfs(0, 1)
# Example usage:
if __name__ == '__main__':
dist = [[0,10,15,20],[10,0,35,25],[15,35,0,30],[20,25,30,0]]
print(tsp_memo(dist)) # Output: 80
Line Notes
memo = [[-1] * (1 << n) for _ in range(n)]Initialize memo table with -1 indicating uncomputed states.
if memo[i][mask] != -1:Return cached result if this state was computed before.
memo[i][mask] = ansStore computed minimum cost for current state.
return dfs(0, 1)Start recursion from city 0 with only city 0 visited.
import java.util.*;
public class TSPMemo {
static int n;
static int[][] dist;
static int ALL_VISITED;
static int[][] memo;
static int dfs(int i, int mask) {
if (mask == ALL_VISITED) return dist[i][0];
if (memo[i][mask] != -1) return memo[i][mask];
int ans = Integer.MAX_VALUE;
for (int j = 0; j < n; j++) {
if ((mask & (1 << j)) == 0) {
int cost = dist[i][j] + dfs(j, mask | (1 << j));
if (cost < ans) ans = cost;
}
}
memo[i][mask] = ans;
return ans;
}
public static void main(String[] args) {
dist = new int[][]{{0,10,15,20},{10,0,35,25},{15,35,0,30},{20,25,30,0}};
n = dist.length;
ALL_VISITED = (1 << n) - 1;
memo = new int[n][1 << n];
for (int[] row : memo) Arrays.fill(row, -1);
System.out.println(dfs(0, 1)); // Output: 80
}
}
Line Notes
static int[][] memo;Memo table to cache results for states.
if (memo[i][mask] != -1) return memo[i][mask];Return cached result if available.
memo[i][mask] = ans;Store computed minimum cost for current state.
memo = new int[n][1 << n];Initialize memo table with size n x 2^n.
#include <bits/stdc++.h>
using namespace std;
int n;
vector<vector<int>> dist;
int ALL_VISITED;
vector<vector<int>> memo;
int dfs(int i, int mask) {
if (mask == ALL_VISITED) return dist[i][0];
if (memo[i][mask] != -1) return memo[i][mask];
int ans = INT_MAX;
for (int j = 0; j < n; j++) {
if ((mask & (1 << j)) == 0) {
int cost = dist[i][j] + dfs(j, mask | (1 << j));
ans = min(ans, cost);
}
}
memo[i][mask] = ans;
return ans;
}
int main() {
dist = {{0,10,15,20},{10,0,35,25},{15,35,0,30},{20,25,30,0}};
n = (int)dist.size();
ALL_VISITED = (1 << n) - 1;
memo.assign(n, vector<int>(1 << n, -1));
cout << dfs(0, 1) << "\n"; // Output: 80
return 0;
}
Line Notes
vector<vector<int>> memo;2D vector to store computed results for states.
if (memo[i][mask] != -1) return memo[i][mask];Return cached result if computed.
memo.assign(n, vector<int>(1 << n, -1));Initialize memo table with -1.
memo[i][mask] = ans;Cache the computed minimum cost.
function tspMemo(dist) {
const n = dist.length;
const ALL_VISITED = (1 << n) - 1;
const memo = Array.from({ length: n }, () => Array(1 << n).fill(-1));
function dfs(i, mask) {
if (mask === ALL_VISITED) return dist[i][0];
if (memo[i][mask] !== -1) return memo[i][mask];
let ans = Infinity;
for (let j = 0; j < n; j++) {
if ((mask & (1 << j)) === 0) {
const cost = dist[i][j] + dfs(j, mask | (1 << j));
if (cost < ans) ans = cost;
}
}
memo[i][mask] = ans;
return ans;
}
return dfs(0, 1);
}
// Example usage:
const dist = [[0,10,15,20],[10,0,35,25],[15,35,0,30],[20,25,30,0]];
console.log(tspMemo(dist)); // Output: 80
Line Notes
const memo = Array.from({ length: n }, () => Array(1 << n).fill(-1));Initialize memo table with -1 for all states.
if (memo[i][mask] !== -1) return memo[i][mask];Return cached result if available.
memo[i][mask] = ans;Store computed minimum cost for current state.
return dfs(0, 1);Start recursion from city 0 with only city 0 visited.
TimeO(n^2 * 2^n)
SpaceO(n * 2^n)
Each state (city, visited mask) is computed once, and for each state, we try up to n next cities.
💡 For n=20, this is about 20 * 1,048,576 = 20 million operations, which is feasible with efficient implementation.
Interview Verdict: Accepted
This approach is the standard solution for TSP with up to 20 cities in interviews.