CS302

September, 2012

Problem description: http://community.topcoder.com/stat?c=problem_statement&pm=12162&rd=15176.

The driver for this is in
**Brick-Main.cpp**,
which includes
**TheBrickTowerMediumDivTwo.cpp**
and compiles the example cases.

When I first read the problem, I thought, "This is brain-dead -- just sort the towers." But then I saw example #3, and it's clear that sorting is not the solution. Or the whole solution. At that point, I looked at the constraints and saw the **heights** will have a maximum size of seven. If I simply generate all possible orderings of the towers, then I can figure out which one has the minimum distance and is lexicographically smallest. Will that be fast enough?

Suppose **heights** has *n* elements.
Generating all possible orderings is equivalent to generating all permutations of towers. It's
the same as permuting the numbers from 0 through *n-1*. How many permutations are there?

When *n* is 7, *n!* is 5040, which is a tiny number
computationally. Our permutation approach should be easily fast
enough. So, here is the strategy for solving the problem:

- Generate all permutations of the numbers from 0 to
**heights.size()-1**. Go ahead and use**next_permutation()**if you want. That will guarantee that you generate the permutations lexicographically. - Turn each permutation into an ordering of the towers. For example, the permutation (0, 2, 1) in Example 0 corresponds to the towers in the following order: (4, 5, 7).
- For each permutation, calculate the total distance between the heights of the towers.
- Store and return the permutation that has the minimum total distance.

I have additional lecture notes below from 2012, where I solved it using recursion rather than

We'll generate the permutations in the exact same way that we did in class. Let's do that first, and we'll solve the problem later.

That code is in
**Brick-1.cpp**, and it is pretty much identical
to the lecture notes on permutations:

#include <string> #include <vector> #include <list> #include <algorithm> #include <map> #include <set> #include <iostream> #include <cstdio> #include <cstdlib> using namespace std; class TheBrickTowerMediumDivTwo { public: vector <int> find(vector <int> heights); vector <int> H; vector <int> I; void Permute(int index); }; void TheBrickTowerMediumDivTwo::Permute(int index) { int i, tmp; if (index == I.size()) { for (i = 0; i < index; i++) cout << I[i] << " "; cout << endl; return; } /* This is the standard recursive permutation -- swap each element in I with the one in index, and call recursively on index+1 */ for (i = index; i < I.size(); i++) { tmp = I[i]; I[i] = I[index]; I[index] = tmp; Permute(index+1); tmp = I[i]; I[i] = I[index]; I[index] = tmp; } } vector <int> TheBrickTowerMediumDivTwo::find(vector <int> heights) { vector <int> rv; int i; H = heights; for (i = 0; i < H.size(); i++) I.push_back(i); Permute(0); return rv; } |

We run it by copying it to **TheBrickTowerMediumDivTwo.cpp** and compiling
**Brick-Main.cpp**. An examination of the first two examples convinces me
of correctness:

UNIX>Now, instead of printing out the indices, let's calculate their distance, and store it if it is minimal. That's incp Brick-1.cpp TheBrickTowerMediumDivTwo.cppUNIX>g++ Brick-Main.cppUNIX>a.out 00 1 2 0 2 1 1 0 2 1 2 0 2 1 0 2 0 1 UNIX>a.out 1 | head0 1 2 3 4 5 6 0 1 2 3 4 6 5 0 1 2 3 5 4 6 0 1 2 3 5 6 4 0 1 2 3 6 5 4 0 1 2 3 6 4 5 0 1 2 4 3 5 6 0 1 2 4 3 6 5 0 1 2 4 5 3 6 0 1 2 4 5 6 3 UNIX>a.out 1 | tail6 0 1 5 3 4 2 6 0 1 5 3 2 4 6 0 1 5 2 3 4 6 0 1 5 2 4 3 6 0 1 2 4 5 3 6 0 1 2 4 3 5 6 0 1 2 5 4 3 6 0 1 2 5 3 4 6 0 1 2 3 5 4 6 0 1 2 3 4 5 UNIX>a.out 1 | wc5040 35280 75600 UNIX>

class TheBrickTowerMediumDivTwo { public: vector <int> find(vector <int> heights); vector <int> H; vector <int> I; void Permute(int index); int min; vector <int> rv; }; #define MAX(a, b) (((a) > (b)) ? (a) : (b)) void TheBrickTowerMediumDivTwo::Permute(int index) { int i, tmp, d; if (index == I.size()) { d = 0; for (i = 1; i < index; i++) d += MAX(H[I[i]], H[I[i-1]]); if (d < min) { min = d; rv = I; /* This throws out the old rv and makes a copy of I. */ } return; } /* This is the standard recursive permutation -- swap each element in I with the one in index, and call recursively on index+1 */ for (i = index; i < I.size(); i++) { tmp = I[i]; I[i] = I[index]; I[index] = tmp; Permute(index+1); tmp = I[i]; I[i] = I[index]; I[index] = tmp; } } vector <int> TheBrickTowerMediumDivTwo::find(vector <int> heights) { int i; H = heights; min = 48*H.size(); for (i = 0; i < H.size(); i++) I.push_back(i); Permute(0); return rv; } |

A few comments about this program. First, the definition of **MAX()** uses the
C preprocessor. Whenever the program does **MAX(a, b)**, the preprocessor
does a text substitution of **(((a) > (b)) ? (a) : (b))**. Why do that instead of
just writing a procedure? Well, it's faster. It won't make a difference here, but
there are times where you'd like to put in something that looks like a procedure, but
really stands for a block of code. Statements like this allow you to do it. When done
correctly, they make your program more readable rather than less. Don't go overboard
with them, but for things like max and min, they can be convenient.

*As an aside, statements like this can backfire. Suppose you do MAX(f(i), f(j)),
where f(i) and f(j) are complex and time-consuming procedure calls. Then
your macro has backfired, because you will be calling one of f(i) or f(j)
twice. Be forewarned.
*

Second, I sentinelize **min** so that it has a larger value than *any* ordering of
the towers. That comes from the problem constraints.
When I run it, it works for the first two examples, but not for the third:

UNIX>Our solution for example 2 gets an ordering with the minimum distance of eight, but the indices are not lexicographically smallest. We'll solve that by writing another method,cp Brick-2.cpp TheBrickTowerMediumDivTwo.cppUNIX>g++ Brick-Main.cppUNIX>a.out 00 2 1 UNIX>a.out 10 1 2 3 4 5 6 UNIX>a.out 20 3 2 1 UNIX>

This solution is in
**Brick-3.cpp**, and I only include the relevant parts
that have changed:

class TheBrickTowerMediumDivTwo { public: vector <int> find(vector <int> heights); vector <int> H; vector <int> I; void Permute(int index); int Less_Than(); int min; vector <int> rv; }; #define MAX(a, b) (((a) > (b)) ? (a) : (b)) int TheBrickTowerMediumDivTwo::Less_Than() { int i; for (i = 0; i < I.size(); i++) { if (I[i] < rv[i]) return 1; if (rv[i] < I[i]) return 0; } return 0; /* This will actually never happen */ } void TheBrickTowerMediumDivTwo::Permute(int index) { int i, tmp, d; if (index == I.size()) { d = 0; for (i = 1; i < index; i++) d += MAX(H[I[i]], H[I[i-1]]); if (d < min || (d == min && Less_Than())) { min = d; rv = I; /* This throws out the old rv and makes a copy of I. */ } return; } ... |

Should you be worried that when you call **Less_Than()** that **rv** is
empty? Probably a little. However, that won't happen, because the first time
that you test a set of indices, its value of **d** will be less than **min**,
and **Less_Than()** will not be called, as C evaluates its booleans from left-to-right,
and stops when it knows that the expression is true or false.

UNIX>Nice -- time to submit!cp Brick-3.cpp TheBrickTowerMediumDivTwo.cppUNIX>g++ Brick-Main.cppUNIX>a.out 00 2 1 UNIX>a.out 10 1 2 3 4 5 6 UNIX>a.out 20 3 1 2 UNIX>a.out 30 6 3 1 2 4 5 UNIX>

- First, it reinforces how to recursively generate permutations.
- Second, it shows once again that an incremental approach to problem-solving works very nicely.
- I've demonstrated a
**#define**paramaterized macro. These are fast, but you have to be careful, as they can backfire. - Again, it shows how to use a sentinel.