One Point Solution

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Saturday, 14 April 2012

Google Code Jam 2012: Qualification round

Posted on 17:58 by Unknown

What a problem D. In an attempt to honor the difficulty spike, the first qualification had very easy problems, until problem D. I spent the whole afternoon coding a solution only to find out right now it was doomed to perish anyway.

Problem A: Speaking in tongues

The algorithmic part is easy. But you have to find out the actual rules to replace the alphabet. I was feeling lucky because it was just recently I participated in codeforces April Fools contest. Because I used the very same python script to quickly translate the input/output specification into a look up table. Some letters were missing, but you could get them from the statement. One letter was still missing, but since it was only one letter, there was only one possibility for what z meant...

I guess the point of this problem was to reward general problem solving skills.

Problem B: Dancing With the Googlers

This one looks like it was meant to test your basic knowledge . It was definitely the most standard of all the problems. B-small could have been solved by mere brute force. As for B-large, I used dynamic programming.

Let us say you have a score a+b+c. Is it possible its maximum is at least X assuming the triplet (a,b,c) is not surprising? Is it possible the maximum is at least X assuming the triplet (a,b,c) is surprising?. You can actually just build a table couldBe[a+b+c][p][surp] that is true if there is a triplet (a,b,c) such that its maximum is at least p, (a+b+c) is the sum and the triplet is surprising if and only if surf=1. Since individual judge scores are at most 10, it is easy to fill this table by actually trying all the triples possible.

After you get that table. You just need to do use the following recurrence: F(n,s, P), the maximum total of googlers with a maximum sub-score that is at least P if S of them were surprising among the N first googlers. So the overall result is F( total googlers, S, P). Now, let us say we want to solve F(n, s,P). We pick googler # (n-1). We know his score, and we can decide to assume his triplet was surprising or not (Assuming the previous table says the combination of total score, maximum and surprise is possible). This will lead to two sub-problems : F(n-1, S, P) and F(n-1, S-1, P) (S could have been reduced if you picked the googler to be surprising). If you solve both smaller sub-problems then you just have to pick the best between each of the choices.

Problem C: Recycled numbers

This seemed easier than B. I guess it just tests your basic optimization skills. C-small is easy and you can just iterate through all the pairs in the range and simulate everything. C-large is still easy: For each x, count the number of ys such that (x,y) is a valid pair. You can build all possible y, since you need to take a left portion of x. Then there are at most log(|x|) (the number of digits) values of y you can pick.

I hesitated for a second in C-large, because maybe my simulation was too slow (I used simple STL code rather than extracting digits using calculations, so constant factor was large). It was not, but I still used two cores just in case. It is great to have a template that can do split input into two processes easily in cases you have doubts-

Problem D: Mirrors of Hell ... Hell of mirrors ... Hall of mirrors

Update: This explanation is incomplete and the formula is wrong. Fear not, cause I have written a long tutorial about this problem, including D-large: It's here

I stopped feeling lucky. My initial plan for this match was to finish it in around 2 hours and then enjoy my perfect score. But this problem was just evil. Evil to think about, evil to implement, evil everything.

I spent a lot of last night trying to solve D-large, because I did not really know how could the 2*W+2*H-4 restriction in D-small would help make it easier. I was baffled, because many coders were solving D-small and not D-large, so it must have been a useful thing... Somehow, I missed for all this time that there was a condition that all of the border columns and rows were going to be full of mirrors. So in fact, D-small is easier because the shape is not irregular and grid-based, but it is just a rectangle.

Unfortunately, the rectangle business does not make the problem TOO easy either. Unless you are familiar with the way rays bounce inside a rectangle. My approach was to try a lot of them until I noticed something quite excellent. For every pair (bx, by) (bx and by could be negative but at least one is non-zero) there is exactly one way to make a ray that starts at (ANY position inside the rectangle) and ends at such position again such that the ray of light bounces bx times on a horizontal mirror and by times on a vertical mirror (Consider those special corners that always return the ray of light as just bouncing once in the vertical side of the corner and once in the horizontal side of the corner). More so, the total length of this trajectory can be found using a (rather clever) formula that you will need some magic to develop. Because of this, you can just pick all relevant pairs (bx,by) (You will later see that because of the constraints and the formula for the total length, abs(bx) and abs(by) can be bounded by 100 or even a lower value). Now, something tricky to understand is that bx and by can be negative. Whatever negative bounces means...

The formula... Let us say you are located at (x0,y0), you want to bounce on horizontal mirrors bx times and on vertical mirrors by times. Now let us imagine you want your trajectory to begin in the first quadraint (important). Draw in some paper how is it like to bounce 6 times on horizontal mirrors and once on vertical mirrors and then return to (x0,y0). You will see something amazing:

The actual formula for the distance depends on (bx,by), as you can see, it is similar to multiplying something that depends on bx * H and something from by * W. There are some conditions like what to do when bx and/or by are odd. In those cases, you have to sort of subtract the original position from the ending portion. All in all, the general formula looks like this: (The idea of using negative bx and by is so that you can make it work for any quadraint as starting trajectory).


int dx, dy;
if (bx & 1) {
dx = W * (bx- sign(bx) ) + 2*( (bx >= 0)?x0:(x0-W));
} else {
dx = bx*W;
}
if (by & 1) {
dy = H * (by - sign(by) ) + 2*( (by >= 0)?y0:(y0-H));
} else {
dy = by*H;
}

Where (dx,dy) is the final point. So that the distance es equal to sqrt(dx*dx + dy*dy). You just need to pick the values of (bx,by) such that the distance is less than or equal to D... Ok, we lied, this formula works great in order to find bouncing shapes that finish and start on a point, but they may visit that point more than once and continue going. The fix to this is to make sure to only consider values of (bx,by) that are not a multiple of a value you already tried (The smaller pair will have smaller distance and the same trajectory, but will finish the first time it re-visits the point).

This is difficult to explain. I recommend taking a paper or a graphics environment and drawing some cases, it becomes clear eventually.

The final code looks like this: Note that I extract the rectangle from the grid and in order to avoid using floats, I multiply all coordinates by 2.

int W, H, D; 
char grid[31][31];

int gcd(int a, int b)
{
if (a < 0) {
return gcd(-a, b);
} else if (b < 0) {
return gcd(a, -b);
} else if (a < b) {
return gcd(b,a);
}
while (b != 0) {
int c = b;
b = a % b;
a = c;
}
return a;
}


#define sign(x) (x/abs(x))
int solveFinal(int W, int H, int D, int x0, int y0)
{

/* ____________________ (W,H)
| |
| |
| |
| (x0,y0) |
| |
|____________________|
(0,0)
*/

set< pair<int,int> > st;
for (int bx = -200; bx < 200; bx++) {
for (int by = -200; by < 200; by++) {
if ( !bx && !by ) continue;


int dx, dy;
if (bx & 1) {
dx = W * (bx- sign(bx) ) + 2*( (bx >= 0)?x0:(x0-W));
} else {
dx = bx*W;
}
if (by & 1) {
dy = H * (by - sign(by) ) + 2*( (by >= 0)?y0:(y0-H));
} else {
dy = by*H;
}

//2*sqrt(dx*dx + dy*dy) <= D

if ( (dx*dx + dy*dy) <= D*D) {
int g = gcd(dx,dy);
int old = st.size();
st.insert( make_pair( dx/g, dy/g ) );
if (old != st.size()) {
cout << "("<<dx/g<<","<<dy/g<<")"<<endl;
}

}
}
}


return st.size();
}

int solve() {
int x0,y0;
for (int i=0; i<W; i++) {
for (int j=0; j<H; j++) {
if (grid[i][j] == 'X') {
x0 = i; y0 = j;
}
}
}
//cout << W <<", "<<H<<" ; "<<x0<<","<<y0<<" ;; "<<D<<endl;
//Convert
x0 = 2*x0 + 1;
y0 = 2*y0 + 1;
x0 -= 2;
y0 -= 2;
W = 2 * (W - 2);
H = 2 * (H - 2);
D *= 2;

return solveFinal(W,H,D,x0,y0);
}

Failing at D-large

So, after finishing D-small, I had a lot of other things to do. Then I tried to solve D-large later in the afternoon. My idea was that using the logic from D-small, you can prove that you only need to try starting direction vectors (dx,dy) where each of the components is between -100 and 100 and dx and dy are co-prime. Then my idea was to simulate the light for each of those starting directions.

I first tried to use fractions, because I have been very worried about precision in this problem. I tried to use gmp. And although I managed to get it to work. Even simple tests were looking quite slow. It was going to be unlikely this would work well under the 8 minutes limit when I finished coding all the logic and stuff. So I tried to do it using doubles being careful about precision.

It took me a lot of time to implement correct logic for this. I extracted line segments and saved them in arrays. Extracted special corners. Squares that can block. I then tried to find the closest intersection between current point (X,Y) and something when using (dx,dy) as a direction vector. It is all equations but it gets tiresome.

When I noticed, 4 hours flew implementing all that stuff. And once I was ready to begin testing cases. There were tons of bugs. I used the last round of the match in trying to find all those bugs and fix them. But eventually, after the end of the match I think I found out that precision issues with my code were so evil that the light was getting really deviated from its real trajectory.

Too bad about not getting a perfect score. A lot of people did. I am thinking there might be a trick like in D-small that works with irregular shapes. Or rather, a general version of the trick.

Update: looking at the contest analysis, it seems there was an even more clever way to solve D-small that truly helped D-large.

Email ThisBlogThis!Share to XShare to Facebook
Posted in explanation, google, googlecodejam | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • TopCoder SRM 557 - finally
    SRM 557 Explanation for division 1 Easy and match recap. Explanations for div2 easy and div2 medium. It feels like it has been ages since t...
  • SRM 589 Editorial
    I have finished writing the editorial for TopCoder SRM 589: http://apps.topcoder.com/wiki/display/tc/SRM+589 . As you most likely noticed. L...
  • SRM 590 recap and editorial
    Another week another Topcoder match. Not a great day. I had a bad flu and still do. Div1 500: The one with Xor Given a list of cards with nu...
  • SRM 546: relief
    I figured I should post something about this SRM. I've been very busy these weeks because the semester is ending and I tried to win a t-...
  • SRM 526: The killing wait for results
    While I wait for results, here is my perspective on this algorithm contest. It began with issues, it had to be postponed 15 minutes. TC has ...
  • SRM 554 div1 hard: TheBrickTowerHardDivOne
    Link to problem statement We got infinitely many bricks of dimensions 1x1x1 and C different colors. Count the number of towers of size 2x2...
  • SRM 533: Div1 500 MagicBoard explanation
    Finally solved it. It is a nice problem that is worth explaining in a post. You have a grid/board of at most 50x50 cells. Some cells contain...
  • Member SRM 505: Part 1
    So, let me explain a couple of problems from a Topcoder Member SRM that I wrote and never got an editorial. BTW, it was the last member SRM....
  • ListedLinks 2012-02-10
    Saturday Morning Breakfast Cereal comics: Grace Hopper's ghost That Oracle engineer blog post Oracle would really not like anyone to se...
  • Codeforces "Good bye 2013" round
    So it was a special round for coders of both divisions, problems ranged from the super easy problem A to the super difficult problems E,F,G....

Categories

  • acm
  • algorithm
  • answers
  • arenaplugin
  • badday
  • behindthescenes
  • bugs
  • c++
  • censorship
  • codechef
  • codeforces
  • contests
  • crocchamp
  • editorial
  • editorial.srm
  • embarrassing
  • explanation
  • gcj2013
  • gmp
  • goodday
  • google
  • googlecodejam
  • greed
  • groklaw
  • health
  • html
  • httpseverywhere
  • implementation
  • ipsc
  • ispc
  • java
  • kawigiedit
  • kindagoodday
  • lamebook
  • languages
  • lego
  • listedlinks
  • marathon
  • nasa
  • offtopic
  • ouch
  • postmortem
  • postportem
  • practical
  • probably_not_a_good_tip
  • problemsetting
  • programming
  • python
  • quora
  • rant
  • recap
  • slightlygoodday
  • snippet
  • srm
  • stl
  • strategy
  • swerc
  • tco
  • tco12
  • tco13
  • tco2012
  • tco2013
  • ternarysearch
  • topcoder
  • tricks
  • ubuntu
  • uva
  • vjass
  • vkcup
  • wc3
  • zinc

Blog Archive

  • ►  2014 (1)
    • ►  January (1)
  • ►  2013 (141)
    • ►  December (14)
    • ►  November (8)
    • ►  October (13)
    • ►  September (11)
    • ►  August (14)
    • ►  July (15)
    • ►  June (13)
    • ►  May (13)
    • ►  April (12)
    • ►  March (11)
    • ►  February (11)
    • ►  January (6)
  • ▼  2012 (94)
    • ►  December (5)
    • ►  October (6)
    • ►  September (8)
    • ►  August (6)
    • ►  July (3)
    • ►  June (5)
    • ►  May (8)
    • ▼  April (10)
      • Google codejam round 1A
      • Evil SRM 541 is evil
      • To solve Google Codejam: Hall of Mirrors II
      • To solve Google Codejam: Hall of Mirrors I
      • To solve Google Codejam: Hall of Mirrors III
      • Google Code Jam 2012: Qualification round
      • Topcoder SRM 540
      • Topcoder open 2012: Round 1B
      • Codeforces Croc Champ 2012 - Round 1
      • Codeforces April Fools Day Contest
    • ►  March (20)
    • ►  February (16)
    • ►  January (7)
  • ►  2011 (51)
    • ►  December (7)
    • ►  November (12)
    • ►  October (5)
    • ►  September (1)
    • ►  August (3)
    • ►  July (4)
    • ►  June (3)
    • ►  May (7)
    • ►  April (3)
    • ►  March (2)
    • ►  February (1)
    • ►  January (3)
  • ►  2010 (9)
    • ►  December (4)
    • ►  October (1)
    • ►  June (1)
    • ►  May (1)
    • ►  January (2)
  • ►  2009 (1)
    • ►  December (1)
Powered by Blogger.

About Me

Unknown
View my complete profile