October 22, 2007
C++ Hash Table Memoization: Simplifying Dynamic ProgrammingA Weightier Example
Demonstrating the value of a technique with a problem like Fibonacci numbers can be less than convincing -- it's pretty easy to devise reasonable solutions to the problems in an ad hoc manner.
For a more substantial example, I've slightly revised a problem from Thomas Cormen et al.'s Introduction to Algorithms (Cambridge: MIT Press, 2001):
A sample version of the org chart is in Figure 2. Keep in mind that this sample is fairly small, but Bill's chart will normally have almost 80,000 employees.
Figure 2: The Excess Weight Org Chart
The employee names are not too imaginative, but you can see that employee a is carrying 1 extra pound, employee b 2, and so on.
Because of the constraints on the problem, we know we can't invite all employees. We need to decide who to invite, but still obey the rule that no employees are there with their supervisors.
This problem is very amenable to a conventional recursive solution. To determine the maximum weight that can be achieved at a given node, we can use something like the pseudocode shown here:
GET-MAX-WEIGHT( node )
if node.number_of_children is 0
return node.weight;
weight1 = 0;
for i = 1 to node.number_of_children
weight1 = weight1 + GET-MAX-WEIGHT( node.child[ i ] );
weight2 = node.weight;
for i = 1 to node.number_of_children
for j = 1 to node.child[ i ].number_of_children
weight2 = weight2 + GET-MAX-WEIGHT( node.child[ i ].child[ j ] )
return max( weight1, weight2 )
The logic is straightforward. At each node we calculate two possible values for the maximum weight. The first value, weight1, defines the maximum weight if the node does not attend. Since the node is not attending, the weight will consist of the sum of all that node's immediate children.
The second value, weight2, is used to calculate the maximum value of the node when the node does attend. Since the node is attending, none of its immediate children can attend, which means the total weight will be the sum of the node's weight, plus the max of all of its children's children.
You can immediately see that this algorithm shares a characteristic with the Fibonacci algorithm. When evaluating a node, we make recursive calls at two different depths in the calling tree, and this results in overlapping subproblem evaluation. For example, when implementing this pseudo codeon the organizational chart in Figure 2, the list of nodes passed in to GET-MAX-WEIGHT will be:
Table 1: The Excess Weight Org Chart
You'll note that there are 17 nodes, but because of duplicates, we call GET-MAX-WEIGHT 31 times, leading to much extra work. And that level of work gets into the seriously excessive levels when faced with the entire Microsoft 80,000 person organizational chart.
So naturally, Dynamic Programming comes to the rescue. It turns out that this particular problem fits into the category well. We have overlapping subproblems, we have optimal substructure, so we can employ DP. u
|
|
||||||||||||||||||||||||||||||
|
|
|
|