Posted on 12th May 2015
Wrapping up with these... The final problem was rather hard, and in the end was an exercise in profiling... As ever, links to: Official Contest Analysis and my code on GitHub.
Problem A, Password Problem: You have typed A
characters of your password, and the probability you typed letter i
correctly is \( p_i \), for i=1,...,A
. You can press backspace from 0 to A
times and type again, or just give up and retype. All your typing will from now on be 100% accurate, and pressing enter counts as a keypress. For each strategy compute the expected number of keypresses needed (if you get the password wrong, you'll have to retype it) and return the lowest expected number of keypresses needed. Your password is B
characters in total.
It's best to draw a table. The "give up now" option always takes B+2
keypresses.
| | 1st error at 1 | 1st error at 2 | ... | 1st error at A | All okay | | Strategy | \( p = 1-p_1 \) | \( p = p_1(1-p_2) \) | ... | \( p = p_1\cdots p_{A-1} (1-p_A) \) | \( p = \prod p_i \) | | Backspace 0 times | B-A+1+B+1 | B-A+1+B+1 | ... | B-A+1+B+1 | B-A+1 | | Backspace 1 time | B-A+1+B+1+2 | B-A+1+B+1+2 | ... | B-A+1+2 | B-A+1+2 | | ... | | Backspace A-1 times | A+B-1+B+1 | A+B-1 | ... | A+B-1 |A+B-1 | | Backspace A times | A+B+1 | A+B+1 | ... | A+B+1 |A+B+1 |
The naive way to compute this takes \(O(A)\) to compute each probability, and \(O(A^2)\) for the table, so \(O(A^3)\) overall, which is too slow. However, there is an obvious diagonal pattern to the table which leads to an obvious \( O(A) \) algorithm. It's easiest just to read the code: a.py.
Problem B, Kingdom Rush: Have games 1...N
and each game requires \( a_i\) stars to play at level 1 and \( b_i \geq a_i \) stars to play at level 2.
If you start with 0 stars, can you play all games to level 2 eventually? If so, what is the minimum number of levels you need to play?
An "almost greedy" algorithm works. If you can play a game at level 2, then there is no reason not to play it (you must do so eventually, and playing only puts you in a stronger position, as you'll have more stars). Otherwise, can you play a level 1 game? If not, the task is impossible. If so, we might have a choice (which I initially missed). Heuristically, play the game with the largest \( b_i \) as this will be "hardest" to play later. Can we make this rigourous?
Problem C, Cruise Control: On a dual carriageway, you are given a load of cars with initial positions, fixed constant speeds, and each 5 units long. They can switch lanes instantly so long as another car is not blocking them. Can the cars continue driving forever, or if not, for how long can they drive before a collision is certain?
Solution: This took me ages, but I'm glad to see the official contest analysis also says that this problem is hard. What killed me in the end was that Python is very slow, especially the Fraction
class.
Fraction
so I could test things like "The distance between two cars is exactly 5 units" without dealing with floating point issues.Fraction
class. Even after factoring this out and running it once per "time event", my solution was still too slow. Digging into the code, the slow part seems to be the gcd
calls each time a new Fraction
object is created. Instead, I hard-coded the two tests I needed ( abs(a-b)<5
and a == b-5
) and immediately the script runs in 2 minutes instead of over 8. See an IPython Notebook for details.What we learn:
- Even for quick coding, I like object oriented thinking, and writing a self-contained class is a good idea.
- Profile! The Fraction
class was so slow that this dwafted other considerations, and implementing some hacks solved this more quickly than finessing the rest of the implementation.