Topcoder SRM 489, D2, 500-point problem

James S. Plank
September, 2011

Problem description:

To solve this problem, you need to break it into the important pieces. As always, it's good to check the constraints, since they sometimes help you figure out if an approach is reasonable. In this case, since roses and lilies have a maximum of 16 elements, you can think "enumeration." In particular, there are 216 potential combinations of packets that you can purchase, and since that's a pretty small number (roughly 64,000), you can enumerate them.

For each combination of packets, you'll have tr roses and tl lilies. The next question is which rectangular areas will correspond to tr+tl flowers. The final question is whether a rectangular area will correspond to one which will make a checkerboard pattern with tr+tl flowers.

Again, it helps to think about the constraints. Each element of roses and lilies is a number between 0 and 10,000, so the maximum number of roses and lilies is 320,000. Suppose we check all possible rectangles -- how would we do that? Let's represent the number of rows in a rectangle with r and the number of columns with c. First we can notice that any rectangle where r > c is equivalent to a rectangle with c rows and r columns, so we can restrict ourselves to rectangles where r ≤ c. Let's enumerate them:

The square root of 320,000 is between 500 and 600, so if we enumerate all possible combinations of packets and all possible rectangles, that is roughly 40,000,000 operations. That may be pushing the limits of Topcoder, but it's close enough to try. It's also a bit pessimistic, because that 320,000 number comes from using all the packets, and only one combination uses all the packets. It will be worth timing the worst case.

Finally, suppose we have a rectangle with r rows and c columns. Can you make a checkerboard of roses and lilies from it? Break it into cases. If c is an even number, then each row must have the same number of roses and lilies. That means that tr must equal tl. The same is true if r is an even number. The only tricky case is when both r and c are odd. Then, the rectangle with r rows and c-1 columns will have an equal number of rows and lillies. The last column will either have one extra rose or one extra lily. Do some examples to convince yourself of this. The code is pretty straightforward -- we'll write a procedure to test:

int is_valid(int r, int c, int tr, int tl)
  if (c%2 == 0 || r%2 == 0) return (tr == tl);
  return (tl == tr+1 || tr == tl+1);

Next, let's perform the enumeration using bit arithmetic, and search for valid r and c. Here's the rest of the code (in SRM-489-D2-500.cpp):

int BuyingFlowers::buy(vector <int> roses, vector <int> lilies)
  int i, e, tr, tl, r, c;
  int min;

  min = 1000000;  // Sentinel

  for (e = 1; e < (1 << roses.size()); e++) {
    tr = 0;
    tl = 0;
    for (i = 0; i < roses.size(); i++) {
      if (e & (1 << i)) {
        tr += roses[i];
        tl += lilies[i];
    for (r = 1; r*r <= (tr+tl); r++) {
      if ((tr+tl)%r == 0) {
        c = (tr+tl)/r;
        if (is_valid(r, c, tr, tl)) if (c-r < min) min = c-r;
  if (min == 1000000) min = -1;
  return min;

We compile and test, and it works on the test cases:

UNIX> g++ SRM-489-D2-500-Main.cpp
UNIX> a.out 0
UNIX> a.out 1
UNIX> a.out 2
UNIX> a.out 3
Do we submit??? No, not yet. Let's add test case #4: 16 packets where every packet is 10,000. This will let us test whether our program is too slow. I've put that into SRM-489-D2-500-Main.cpp:
UNIX> time a.out 4
0.188u 0.001s 0:00.18 100.0%	0+0k 0+0io 0pf+0w
Good -- it's fast enough, and is that answer correct? Well, 10,000 is a perfect square, so any case with two packets will have 40,000 seeds, which is a perfect square, and since tl == tr, our difference is indeed 0.


Could we make this faster? Of course. If tl doesn't equal tr, then we only need to test odd rows and columns. That reduces our search space by a factor of two.

In a more difficult vein, we could prime-factor tr+tl, and then test all subsets of the prime factorization. That would give us another reason to use bit arithmetic! Fortunately, for the purposes of Topcoder, we don't have to; however, were we writing production code that did something of this ilk, the prime factorization may be the better way to go.