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