The basis of this algorithm lies with dynamic programming. As with all dynamic programming algorithms we need to first define the DP state. The key factor in this algorithm is to consider an intermediate node k of which to connect two vertices i and j. We check if the path from i to k and then from k to j improves our current shortest path length from vertex i to j. If it does, we update - otherwise we don't. Using this definition our DP state becomes:
D(i,j,k) = shortest path length from vertex i to j considering a set of intermediate nodes from vertex 1 to k
We don't need to explicitly label our vertices as this is done implicitly through our algorithm. Now we need to define our recurrence relation. For a given instance of D(i,j,k) we can calculate the shortest path by considering all intermediate vertices from 1 to k. The proposed distance is easily calculated as D(i,k,k-1) + D(k,j,k-1) by definition of an intermediate node. Formally,
F(i,j,k) = min { F(i,j,k-1), F(i,k,k-1) + F(k,j,k-1) }
We have to determine whether or not the new intermediate node k actually helps achieve a shortest path by comparing it to the previous computation without considering intermediate node k, i.e. F(i,j,k-1).
As with all recurrence relations, a base case is required. If we don't consider any intermediate nodes at all, then the shortest path between two vertices is simply the direct edge between vertex i and j. In other words, the weight of the (i,j) entry in the adjacency matrix. So now we have fully defined our recurrence relation with the proper base case:
F(i,j,k) = min { F(i,j,k-1), F(i,k,k-1) + F(k,j,k-1) }
F(i,j,0) = w(i,j)
To calculate this in a bottom-up manner is actually quite simple. First we note the order dependency of the recurrence relation. It can be seen that a particular instance D(i,j,k) can depend on D(i,0,k-1) to D(N,j,k-1) where N is the number of vertices. So it's obvious we need to compute the previous intermediate k values first (i.e. 1, 2, .., k-1 before calculating k). The order of i and j does not matter in our case because we would have computed all k-1 (i,j) pairs before. Using this information we yield an extremely elegant solution:
// warshall's algorithm for (int k = 1; k <= N; k++) { for (int i = 1; i <= N; i++) { for (int j = 1; j <= N; j++) { adjmat[i][j] = min(adjmat[i][j], adjmat[i][k] + adjmat[k][j]); } } }
Let's apply this algorithm to a simple graph theory problem.
TopCoder SRM 301 D1-450 (EscapingJail)
URL: http://www.topcoder.com/tc?module=ProblemDetail&rd=9822&pm=6222
The problem states there are a set of prisoners chained up and we need to determine the maximum distance that a pair of prisoners can be from each other. It's obvious that the maximum length that two prisoners can be apart is defined by the shortest chain separating a connected path between the two prisoners. Sound familiar? This is the exact definition of a shortest path problem! We simply need to find the shortest distance between every pair of prisoner and calculate the maximum of all such pairs.
How do we handle cases in which the prisoners are not chained together (i.e. there is no edge between them)? We can set a sentinel value to denote that the prisoners aren't chained, if we set this to 0 it would be wrong because then it would interfere with our minimum distance calculation. A simple option is to set this to a high/infinity value and if we know the distance from prisoner i to prisoner j exceeds or equals this value then we know they are unbounded. For this problem, we need to return a value of -1 for unbounded distance.
The only remaining part of the problem is translating the given string array into the adjacency matrix. This is a simple exercise of converting a character into the suitable value - if it's a space then we set the distance to be infinity since they aren't chained together. All that leaves is to use the Warshall algorithm and compute the maximum of the pairs (and remembering to return -1 if the distance between at least two prisoners is unbounded).
The implementation is as follows:
class EscapingJail { public: int getMaxDistance(vector <string>); }; #define INF (1 << 27) int translate(char ch) { if (isdigit(ch)) return ch - '0'; if (islower(ch)) return ch - 'a' + 10; if (isupper(ch)) return ch - 'A' + 36; return INF; } int adjmat[51][51]; int EscapingJail::getMaxDistance(vector <string> chain) { int res = 0; for (int i = 0; i < chain.size(); i++) { for (int j = 0; j < chain[i].size(); j++) { adjmat[i][j] = translate(chain[i][j]); } } for (int k = 0; k < chain.size(); k++) { for (int i = 0; i < chain.size(); i++) { for (int j = 0; j < chain.size(); j++) { adjmat[i][j] = min(adjmat[i][j], adjmat[i][k] + adjmat[k][j]); } } } for (int i = 0; i < chain.size(); i++) { for (int j = 0; j < chain.size(); j++) { if (i == j) continue; if (adjmat[i][j] >= INF) return -1; res = max(res, adjmat[i][j]); } } return res; }
No comments:
Post a Comment