|
| 1 | +# Bitmask Dynamic Programming |
| 2 | + |
| 3 | +## What is Bitmask DP? |
| 4 | + |
| 5 | +Bitmask DP is a type of dynamic programming that uses _bitmasks_, in order to keep track of our current state in a problem. |
| 6 | + |
| 7 | +A bitmask is nothing more than a number that defines which bits are _on_ and _off_, or a binary string representation of the number. |
| 8 | + |
| 9 | +## Traveling Salesman Problem |
| 10 | + |
| 11 | +The traveling salesman problem is a classic algorithmic problem defined as follows: given a list of _N_ cities, as well as the distances between each pair of cities, what is the shortest possible route that visits each city exactly once and returns back to the city that you started from? (_Note_: we can start from any city as long as we return to it) |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +### Take I - Permutations |
| 16 | + |
| 17 | +We can create a permutation of the _N_ cities to tell us what order we should visit the cities. |
| 18 | + |
| 19 | +For example, if we have _N_ = 4, we could have the permutation {3, 1, 4, 2}, which tells us to visit the cities in the following order: 3 --> 1 --> 4 --> 2 --> 3 |
| 20 | + |
| 21 | +For a given _N_, there are _N!_ possible permutations (_N_ choices for the first city, _N-1_ choices for the second city, _N-2_ choices for the third city, etc.) |
| 22 | + |
| 23 | +However, if _N_ > 12, this algorithm will be too slow, so we need to shoot for something better |
| 24 | + |
| 25 | +### Take II - Bitmask DP |
| 26 | + |
| 27 | +Let's use bitmasks to help us with this problem: we'll construct a bitmask of length _N_. |
| 28 | + |
| 29 | +What does the bit at index _k_ represent in the scope of the problem? What does it mean for the bit to be zero? What about one? |
| 30 | +- if the bit at index _k_ is zero, then we _have not yet_ visited city _k_ |
| 31 | +- if the bit at index _k_ is one, then we _have_ visited city _k_ |
| 32 | + |
| 33 | +For a given bitmask, we can see which cities we have left to travel to by checking which bits have a value of zero. |
| 34 | + |
| 35 | +We can construct a recursive function to find the answer to this problem: |
| 36 | + |
| 37 | +```java |
| 38 | +// The following function will calculate the shortest route that visits all |
| 39 | +// unvisited cities in the bitmask and goes back to the starting city |
| 40 | +public double solve( int bitmask ) |
| 41 | +{ |
| 42 | + if ( dp[ bitmask ] != -1 ) |
| 43 | + return dp[ bitmask ]; |
| 44 | + // Handle the usual case |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +What are some base cases for this problem? How do we know when we are done? |
| 49 | + |
| 50 | +What does the bitmask look like when we have visited every city? |
| 51 | +- 111...111 |
| 52 | + |
| 53 | +In this case, we need to return to our starting city, so we'll just return the distance from our current location to the start city, but what is our current location? |
| 54 | + |
| 55 | +Imagine we currently have the bitmask 11111. |
| 56 | + |
| 57 | +This bitmask tells us that we have visited all _N_ = 5 cities and can return home. |
| 58 | + |
| 59 | +However, this bitmasks _does not_ tell us which city we are currently at, so we don't know which distance to return. |
| 60 | + |
| 61 | +This means that in addition to the bitmask, we will need to keep track of the _current location_ (anytime we travel to a city, that city becomes the current location). |
| 62 | + |
| 63 | +```java |
| 64 | +// The following function will calculate the shortest route that visits all unvisited |
| 65 | +// cities in the bitmask starting from pos, and then goes back to the starting city |
| 66 | +public double solve( int bitmask, int pos ) |
| 67 | +{ |
| 68 | + if ( dp[ bitmask ][ pos ] != -1 ) |
| 69 | + return dp[ bitmask ][ pos ]; |
| 70 | + // Handle the usual case |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +If we know that `solve( bitmask, pos )` will give us thge answer to the traveling salesman problem, and we say that we start at city 0, what should our initial function call be? |
| 75 | + |
| 76 | +What is the value of `bitmask`? |
| 77 | +- 1 (we visited city 0, so our starting bitmask is 000...0001) |
| 78 | + |
| 79 | +What is the value of `pos`? |
| 80 | +- 0 |
| 81 | + |
| 82 | +So how do we handle the usual case? |
| 83 | + |
| 84 | +We have a bitmask that tells us which cities we have left to visit, and we know our current position, so which city do we go to next? |
| 85 | +- whichever one will minimize the remaining route |
| 86 | + |
| 87 | +To see which city will minimize the value we want, we need to try _all_ possible remaining cities and see how it affects the future routes. |
| 88 | + |
| 89 | +What does trying to visit a city look like? |
| 90 | + |
| 91 | +Let's say that we are visiting some city at index _k_: |
| 92 | + |
| 93 | +```java |
| 94 | +int newBitmask = bitmask | ( 1 << k ); |
| 95 | +double newDist = dist[ pos ][ k ] + solve( newBitmask, k ); |
| 96 | +``` |
| 97 | + |
| 98 | +We need to update our bitmask, marking city _k_ as visited, and then we add the distance from our current loication to city _k_, and then solve the subproblem where we find the shortest route starting at city _k_ and solving for the rest of the cities. |
| 99 | + |
| 100 | +If we do this for each city that hasn't been visited yet, we want to take the minimum `newDist` that we find. |
| 101 | + |
| 102 | +```java |
| 103 | +// The following function will calculate the shortest route that visits all unvisited |
| 104 | +// cities in the bitmask starting from pos, and then goes back to the starting city |
| 105 | +public double solve( int bitmask, int pos ) |
| 106 | +{ |
| 107 | + // If we have solved this subproblem previously, return the result that was recorded |
| 108 | + if ( dp[ bitmask ][ pos ] != -1 ) |
| 109 | + return dp[ bitmask ][ pos ]; |
| 110 | + |
| 111 | + // If the bitmask is all 1s, we need to return home |
| 112 | + if ( bitmask == ( 1 << N ) - 1 ) |
| 113 | + return dist[ pos ][ 0 ]; |
| 114 | + |
| 115 | + // Keep track of the minimum distance we have seen when visiting other cities |
| 116 | + double minDistance = 2000000000.0; |
| 117 | + |
| 118 | + // For each city we haven't visited, we are going to try the subproblem that arises from visiting it |
| 119 | + for ( int k = 0; k < N; k++ ) |
| 120 | + { |
| 121 | + int res = bitmask & ( 1 << k ); |
| 122 | + |
| 123 | + // If we haven't visited the city before, try and visit it |
| 124 | + if ( res == 0 ) |
| 125 | + { |
| 126 | + int newBitmask = bitmask | ( 1 << k ); |
| 127 | + |
| 128 | + // Get the distance from solving the subproblems |
| 129 | + double newDist = dist[ pos ][ k ] + solve( newBitmask, k ); |
| 130 | + |
| 131 | + // If newDist is smaller than the current minimum distance, we will override it here |
| 132 | + minDistance = Math.min( minDistance, newDist ); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + // Set the optimal value of the current subproblem |
| 137 | + return dp[ bitmask ][ pos ] = minDistance; |
| 138 | +} |
| 139 | +``` |
0 commit comments