Saturday, November 22, 2014

Light switching puzzle w/ solution


A version of light-switching problem goes like this: N prisoners will be separated from each other without communication. They will be selected one at a time at random to enter a room with a switch. The only thing they can do while staying there is to move the switch if they like, and then they will leave the room no matter what. The process goes on and on indefinitely. If at some point anyone of them can ascertain that all of them have entered the room at least once, and it's true, then they'll all be freed. The initial state of the switch is unknown. Given a sufficiently long period of time each of them will enter the room as many times as they wish.  Now, they can meet once before the process starts to figure out a strategy that guarantees their freedom at some point in the future!






Scroll down for solution...























A more common solution is to assign a counter among them before hand. The counter only turns off the switch when it's on, and counts the number of time he/she does so. The rest turns the switch on when it's off. Each of them does so 2 times to counter the fact that the initial state of the switch is unknown. When the counter counts 2N-2 times, he/she knows everyone else has been to the room.

Interestingly, there's an alternative solution without having the counter counts to 2N-2. It is posted here. Below is my explanation/proof that it works.

The only changes of status of the switch are made by the counter, who does so every time, and by the rest switching it off when transitioning from READY or WAITING mode to DONE mode, as depicted in the solution strategy. Because these non-counter moves all turn the switch off, there will never be multiple of them between counter's moves. So the counter will only counts to at most N-1. Now why is this solution robust to the unknown initial state of the switch? It suffices to show that every such non-counter move will be counted by the counter. Why?  Because every non-counter only turns the switch off when in READY mode, and yet they only enters READY mode when they observe the state change of the switch.  Thus, the first ever move of the switch must be made by the counter, so he/she will capture every non-counter move without any uncertainty as opposed in the common solution due to the unknown initial state. Finally, the counters moves the switch everytime to make sure that all the rest will be triggered to transition from the initial WAITING mode to READY mode.

Tuesday, September 23, 2014

Robot

In this post we present a coding problem and our solution which is different from the author's. Our solution doesn't involve tree, and only a stack is used.

https://www.hackerrank.com/contests/w10/challenges/robot

The original problem was phrased in psuedo code. In my wording it becomes:

A robot is to travel from station 0 to station 1, then to station 2, station 3, and so on, to station N - 1. It enters station 0 with energy 0 and no money. At each station i, the robot has two options: either it resets its energy to P[i] >= 0, or it takes money V[i] >= 0. As it enters next station, its energy deceases by 1. The only restriction is that its energy should never becomes negative. The goal is to maximize the total amount of money gathered. 1 <= N <= 500000, 0 <= V[i] <= 10^9, 0 <= P[i] <= 100000. It is guaranteed there's a way that energy is always non-negative.

A standard DP with state (station, energy) takes O[N * max_energy]. Let D(i, e) be the maximum amount of money to be gained when entering station i with energy e. Clearly

D(i, e)
= max{
max money gained if reset energy at station i,
max money gained if take V[i] at station i
}
= max{
P[i] == 0 ? -1 : D(i + 1, P[i] - 1),
e == 0 ? -1 : V[i] + D(i + 1, e - 1)
}

As the complexity is too high, we have to try reducing it. Let's observe an example and see what we got.

N = 5

P: 3 0 1 3 1
V: 2 1 3 6 9

===========================================================================

Our DP starts from i = 4. Because there's no concern of energy after entering the last station, D(4, e) are

9 9 9 9 9 9 9....

===========================================================================

For i = 3, either we take energy 3 and enter i = 4 with energy 2 which will end
up with 9 dollars, or we take 6
dollars if we enter with positive energy and end up with 6 + 9 = 15 dollars. So we're comparing these two rows:

  9  9  9  9  9  9  9 ....
    15 15 15 15 15 15 ....

which becomes
  9 15 15 15 15 15 15 ...

===========================================================================

For i = 2, either we take energy 1 and enter i = 3 with energy 0 which will end up with 9 dollars, or we take 3 dollars if we enter with positive energy and end up with 3 more dollars than i=3. So we're comparing these two rows:

  9  9  9  9  9  9  9 ...

    12 18 18 18 18 18 ...

which becomes
  9 12 18 18 18 18 18 ...

===========================================================================

As we see now, the pattern of update is to compare 2 rows. The first is obtained by expanding D(i + 1, P[i] - 1) to a row. The second is by right shifting the row of D(i + 1, e) by 1 and add V[i]. Adding V[i] is expensive because the row can be long, so why not store V[i] + V[i + 1] +...+ V[N - 1] - earning in the table? This is exactly the amount of money we don't manage to get from station i to the end. This is the first observation.

The second observation is that the number of distinct values increases by at most 1, and is bounded by N - i. So what if we just store a vector or stack of segments with size no more than O(N)? One issue follows immediately: because of the right shift we have to update the end points of these segment in O(N) for each i. But since the right shift always occurs, what about storing end point - (N - i) as the coordinate of the segment? If a segment is not "interrupted", this value remains constant across stations and needs no update. In this way no O(N) update is necessary. In each iteration, the main task is to find the value of D(i + 1, P[i] - 1) because it's not immediately available -- we have to perform O(log(N)) binary search of P[i] - 1 on the end points of the segment. Once found, updating the stack takes amortized cost of O(1).

In brief, with these two coordinate transformations this problem is solved in O(N*log(N)) time and O(N) memory without tree structure. The code is available at https://github.com/paritystsai8/coding_problem/blob/master/robot.cpp

Tuesday, September 16, 2014

Cross Matrix

Weeks ago I ran into this algorithmic problem:

https://www.hackerrank.com/contests/w8/challenges/cross-matrix

Given a binary matrix up to 1500x1500, you are to compute the number of pairs of all-1 rectangles that overlap yet neither of them is completely contained by the other. We have to output this number mod a big fixed number M=1000000007. The time limit for C/C++ is 4 seconds.

My journey with this problem was a bit long. During the contest I submitted a version v0 that failed at two test cases. After the contest I got another version v1 that passed! But I found that the most nasty test case is missing, which my version still fails to solve within time limit, and author's solution passes just fine! With some brain storm, I came up with the solution v2 that nails the worst case as well.

===================== v0: O(N^3) ==========================

The problem itself is a bit intimidating. Given the size limit 1500, we can imagine an O(N^3) algorithm will probably exceed the time limit. However the number of rectangles itself is O(N^4), and not to mention the goal is to compute the number of rectangle pairs!

After some thought, it seems directly counting the pairs that satisfy the condition is hard, as there're many possible relative positions among the two. So, why not count the pairs that DON'T satisfy the condition?

Let's first define some variables:

n_rec: #rectangle
n_total_pair: #rectangle pairs=C(n_rec, 2)
n_disjn_pair: #disjoint pairs
n_intra_pair: #pairs that one completely contains the other

From now on by rectangle we mean all-1 rectangle.

At high level, the answer we're seeking is n_total_pair-n_disjn_pair-n_intra_pair. First, a O(N^2) preprocessing gives us the max number of consecutive 1s we can get any give cell (x, y) and direction d if we walk from (x, y) along d.

Moreover, we want to get these values as well:

N(y): #rectangle north of line Y=y AND touches line Y=y
E(x)
S(y)
W(x)

And the following can be obtained by accumulating values above in the appropriate direction and order in O(N):
Nacc(y): #rectangle north of line Y=y
Eacc(x)
Sacc(y)
Wacc(x)

NE(x,y): #rectangle northwest of (x,y) AND touches line Y=y and X=x
SE(x,y)
SW(x,y)
NW(x,y)

And the following can be obtained by accumulating values above in the appropriate direction and order in O(N^2):
NWacc(x,y): #rectangle northwest of (x,y)
SEacc(x,y)
SWacc(x,y)
NWacc(x,y)

Why do we need them? Because they essentially tell us the number of pairs that can be split horizontally, vertically, and both, and n_disjn_pair can be computed as

sum_x[E(x)*Wacc(x-1)] + sum_y[S(y)*Nacc(y+1)] -
sum_{x,y}[SW(x,y)*NEacc(x+1,y+1)] - sum_{x,y}[SE(x,y)*NWacc(x-1,y+1)]


So, in order to get these numbers, we can do this O(N^3) scan:

for each row y
  for each segment x0, x1 s.t. x0 <= x1
      update n_rec
      update n_intra_pair
      update N(y), S(y), E(x), W(x)
      update SE(x,y), SW(x,y), NE(x,y), NW(x,y)
  end
end

At each position, we're counting the rectangles with one side (x0,y)--(x1,y). After this scan we'll get n_rec, and compute n_total_pair=C(n_rec,2), and then update those accumulated stats and get n_disjn_pair.

What remains is the core part: how do we update n_rec, n_intra_pair, and etc.? At each (y, x0, x1) we know h=the maximum number of rows from Y=y upwards that have all 1s from x0 to x1, i.e. it boils down to a rectangle with dimensions w=x1-x0+1 and h, so we'll add A=w*h to n_rec, N(y), E(x0), W(x1), NE(x0,y), NW(x1,y).

How about n_intra_pair? With some mathematics, you will find that we should add C(w+1,2)*C(h+2,3) to it. And since it actually counts self-identical rectangles, we'll just subtract n_rec from the answer at the end. Note that, it is with the math that we can count much more "aggregatively" and thus much more efficiently.

===================== v1: O(N^3) only for worst case  ==========================

Solution v0 is not too smart, because it scans every (x0, x1) out of C(N,2) combinations, so we have to get rid of it. How do we precisely represent the status as we scan? Imagine we scan upwards column by column, and look at the maximum number of consecutive 1s to the right. The numbers of such 1s at previous cell is all we need: below we'd like to store (4,5,3,3,1,6) before we enter the next cell.

1111
11111
111
111
1
111111

This is not concise enough. In fact some info are redundant. For instance the 5 is not as meaningful because there's a 4 before it, i.e. any attempt to make a rectangle will be restricted by the 4 first. Similarly the last 6 doesn't help more than just 1. Intuitively, we only need to keep a decreasing stack of numbers (4,4,3,3,1,1). Even more concise is (4,3,1) with their number of replicas.

With this stack, how should we update n_rec, n_intra_pair, etc.?  First of all, unlike v0 here at each position we're counting the rectangles with SE corner at (x,y), if we scan in the same direction and order as v0. You'll observe that except for n_intra_pair, all we need to do is to add the sum of this stack to n_rec and other appropriate numbers. In the example above we'll have to add 4+4+3+3+1+1=16. Although the stack will get updated as we scan, we can store an array of prefix sums in another array (2,8,16), so that getting the sum to add takes O(1). What is the cost to update the stack? My first thought is to do
binary search with O(log(N)), but later I notice in the author's solution that simply popping the stack until its top is strictly less than the current max number of consecutive 1s will give an amortized cost of O(1).

Now, everything looks great except for n_intra_pair. It didn't seem possible to update it quickly with just this stack and prefix sum, so I just compute sum[C(h+1,2)*C(w+1,2)] in the stack with O(N). Therefore unfortunately the time complexity of this version is still O(N^3). But what's the case that really takes O(N^3)? It'll be something like 1s and 0s divided by a diagonal, and it seems missing the author's test case though, so this solution passed.

===================== v2: O(N^2) ==========================

Now the only thing we have to deal with is n_intra_pair, which we still computed in O(N^3). The reason we couldn't do it more efficiently was there doesn' seem to be a simple prefix sum that'd work and is similar to the one we used to store the partial sum in
the stack. I got stuck with this for a really long time, until I decided to put everything in math formula with absolute cooridates on the paper.

Let's assume we have a stack y[] of max number of consecutive 1s, and their absolute positions x[], where x[0] = -1 and y[0] won't be used ever. In the following example, x[] = {-1,3,5,7}, y[] = {?,2,4,6}

111111
1111111
1111
11111
11
111
...
....
---------matrix boundary


As before, combinatorics tells us that the total addition to n_intra_pair with this stack with length K is

sum_{i=1}^{K}[C(y_i+2,3)*[C((x_K-x_{i-1})+2,3)-C((x_K-x_i)+2,3)]]

Expanding this gives us three terms:

A=sum_{i=1}^{K}f0(x_i,x_{i-1},y_i)

B=sum_{i=1}^{K}f1(x_i,x_{i-1},y_i)*x_K

C=sum_{i=1}^{K}f2(x_i,x_{i-1},y_i)*x_K^2

We can therefore keep 3 prefix sums for A, B/x_K, C/x_K^2, and sum them up after
multiplying with x_K and x_K^2 appropriately in O(1). So now, n_intra_pair takes O(N^2) too.

And that's it! Easy enough to verify the correctness of the code and efficiency against the worst test case, the problem is totally solved. As mentioned by the author, it is indeed a good problem to train the skill of counting, patience, and math skill. Looking forward to another interesting problem next time!