SRM 604, D2, 500-Pointer (PowerOfThreeEasy)

James S. Plank

Original Writeup: Thu Sep 15 15:51:35 EDT 2016
Latest modification: Wed Sep 20 16:38:44 EDT 2023

In case Topcoder's Servers are Down

Here is a description of the problem.

Examples

#          X          Y  Return Value
- ---------- ----------  ------------
0          1          3  "Possible": Right at step 0, up at step 1.
1          1          1  "Impossible"
2          3          0  "Impossible"
3          1          9  "Impossible"
4          3         10  "Possible"
5       1093       2187  "Possible"
6          0          0  "Possible"

Testing yourself

Like the Cryptography Problem, I have a shell script tests.sh, that you can use to test your program. When you run tests.sh, your answer should be identical to answers.txt.


Hints

There's a very simple solution of this problem that does not involve enumeration, but suppose you're doing this program during a programming competition, and the simple solution doesn't come to you. This is in fact what happened to me when I first worked on this problem, and the enumeration did come to me quickly. Fortunately, it was fast enough! So, while it doesn't get points for elegance, it does get points for solving the problem!

You can view this as a power set enumeration problem. Your set consists of the numbers { 1, 3, 9, 27, ..., 3z }, such that the sum of all of the numbers in the set equals x+y. These are all of the horizontal and vertical moves that the robot can make. If there is no set of this form whose sum equals x+y, then you can't solve the problem, and you return "Impossible" instantly.

Otherwise, once you determine this set, you enumerate its subsets. For each subset S, you add up the elements in S to see if you get x. If so, you know that the sum of the elements not in S equals y, and you can return "Possible."

If, after enumerating each subset, you don't get a "Possible" answer, then it's "Impossible."


So, let's break your answer into parts. The first part is calculating the set of numbers. In my code that was a vector of ints, which I called TheSet. To calculate TheSet, start with one, and keep generating successive powers of three. Add them to TheSet, so long as the sum of the elements of TheSet is less than x+y.

At the end of this process, either the sum of the elements of TheSet equals x+y, meaning that you can start your power set enumeration, or the sum is greater than x+y, meaning that you can return "Impossible."

Here's the code for this part (the whole solution is in Solution.cpp).

string PowerOfThreeEasy::ableToGet(int x, int y)
{
  vector <int> TheSet;  /* This will hold the elements of the set. */
  long long d, sum;
  int i, j;

  if (x == 0 && y == 0) return "Possible";

  /* First, calculate TheSet.  The elements here are successive powers 
     of three such that their sum equals x+y. */

  d = 1;
  sum = 0;

  do {
    TheSet.push_back(d);
    sum += d;
    d *= 3;
  } while (sum < x + y);
  
  /* When we get to this point, either sum > x+y, which means that 
     it's impossible, or sum = x+y, and we can move onto the enumeration. */

  if (sum != x + y) return "Impossible";

We now perform the power set enumeration, which follows the lecture notes on enumeration exactly. Each subset is held in the integer i, and element j is in subset i if the j-th bit of i is set.

For each subset, we add up the elements, and if they equal x, then the answer is "Possible." If we can't find an answer, then it is "Impossible."

Here is the remainder of the code:

  /* Do the power set enumeration.  For each subset of TheSet, add up the
     elements of the subset.  If they equal x, then it's "Possible" and
     you can return.  Otherwise, try the next subset. */

  for (i = 0; i < (1 << TheSet.size()); i++) {
    sum = 0;
    for (j = 0; j < TheSet.size(); j++) {
      if (i & (1 << j)) sum += TheSet[j];
    }
    if (sum == x) return "Possible";
  }

  /* If we couldn't find a subset, then return "Impossible". */

  return "Impossible";
}


What's the faster solution?

If you look at x+y in base three, it should be 11111111... So, take each of x and y mod three -- exactly one of them should be one, and exactly one of them should be zero. Div them both by three and repeat. That's a much faster and more elegant solution.

It's in Solution-Elegant.cpp:

string PowerOfThreeEasy::ableToGet(int x, int y)
{
  while (x != 0 || y != 0) {
    if ((x % 3) + (y % 3) != 1) return "Impossible";
    x /= 3;
    y /= 3;
  }
  return "Possible";
}