-
Notifications
You must be signed in to change notification settings - Fork 21.2k
Expand file tree
/
Copy pathTravelingSalesmanBitmask.java
More file actions
107 lines (91 loc) · 3.73 KB
/
Copy pathTravelingSalesmanBitmask.java
File metadata and controls
107 lines (91 loc) · 3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package com.thealgorithms.dynamicprogramming;
import java.util.Arrays;
/**
* Solves the Traveling Salesman Problem using bitmask dynamic programming.
*
* <p>The DP state is {@code dp[mask][city]}, where {@code mask} stores the set of
* visited cities and {@code city} is the current endpoint of the partial tour.
* This is the classic Held-Karp formulation and is practical for small inputs
* where {@code n <= 20}.</p>
*
* <p>This bitmask version complements {@code graph/TravelingSalesman.java} by
* showing the subset-DP formulation explicitly, which is useful for small
* instances and for problems that require visiting subsets in a specific order.</p>
*/
public final class TravelingSalesmanBitmask {
private static final int INF = Integer.MAX_VALUE / 4;
private TravelingSalesmanBitmask() {
}
/**
* Computes the minimum Hamiltonian cycle cost starting and ending at city 0.
*
* <p>The input must be a square matrix. Use {@link Integer#MAX_VALUE} for
* missing edges. The method returns {@code 0} when no Hamiltonian cycle exists.
*
* @param distanceMatrix square matrix of edge weights; use {@link Integer#MAX_VALUE}
* for missing edges
* @return the minimum tour cost, or 0 when no tour exists
* @throws IllegalArgumentException if the matrix is not square
*/
public static int solve(int[][] distanceMatrix) {
if (distanceMatrix == null) {
throw new IllegalArgumentException("Distance matrix cannot be null");
}
if (distanceMatrix.length == 0) {
return 0;
}
int n = distanceMatrix.length;
for (int[] row : distanceMatrix) {
if (row == null || row.length != n) {
throw new IllegalArgumentException("Matrix must be square");
}
}
int[][] dp = new int[1 << n][n];
for (int[] row : dp) {
Arrays.fill(row, INF);
}
dp[1][0] = 0;
for (int mask = 1; mask < (1 << n); mask++) {
for (int currentCity = 0; currentCity < n; currentCity++) {
if (!isBitSet(mask, currentCity) || dp[mask][currentCity] == INF) {
continue;
}
for (int nextCity = 0; nextCity < n; nextCity++) {
if (isBitSet(mask, nextCity) || distanceMatrix[currentCity][nextCity] == Integer.MAX_VALUE) {
continue;
}
int nextMask = setBit(mask, nextCity);
int newCost = safeAdd(dp[mask][currentCity], distanceMatrix[currentCity][nextCity]);
if (newCost == INF) {
continue;
}
if (newCost < dp[nextMask][nextCity]) {
dp[nextMask][nextCity] = newCost;
}
}
}
}
int fullMask = (1 << n) - 1;
int bestTour = INF;
for (int lastCity = 1; lastCity < n; lastCity++) {
if (dp[fullMask][lastCity] == INF || distanceMatrix[lastCity][0] == Integer.MAX_VALUE) {
continue;
}
bestTour = Math.min(bestTour, safeAdd(dp[fullMask][lastCity], distanceMatrix[lastCity][0]));
}
return bestTour == INF ? 0 : bestTour;
}
private static boolean isBitSet(int mask, int bit) {
return (mask & (1 << bit)) != 0;
}
private static int setBit(int mask, int bit) {
return mask | (1 << bit);
}
private static int safeAdd(int left, int right) {
if (left == INF || right == Integer.MAX_VALUE) {
return INF;
}
long sum = (long) left + right;
return sum >= INF ? INF : (int) sum;
}
}