Editorial for Mock CCC '15 S4 - Ice Pillars
Submitting an official solution before solving the problem yourself is a bannable offence.
Approach 1 — Brute Force
Generate all permutations in which to break the blocks. Simulate the chain reactions when they occur.
This is enough to obtain of the points.
Time Complexity:
Approach 2 — Divide and Conquer
A faster solution uses memoization on a divide and conquer principle. The subproblem is determining the cost to knock down every pillar in the range given whether pillar is broken and whether pillar is broken.
- In the base case, , and there is only 1 pillar to consider. The cost to knock it down is just its durability minus the possible damages done by the pillars to its left and right.
- Otherwise, if there is more than 1 pillar to consider:
- If pillar is broken and completely knocks over pillar without you having to do any work, then we can narrow the range by 1 from the left and mark the newest left outside-border pillar as broken.
- If pillar is broken and completely knocks over pillar without you having to do any work, then we can narrow the range by 1 from the right and mark the newest right outside-border pillar as broken.
- Otherwise, we test every pillar between and as the first pillar to knock down, taking into account collateral damage. The total cost is the cost to break down this pillar, plus the cost to break all the pillars to the left of it, plus the cost to break all of the pillars to the right of it. In other words, we divide the range in half and let recursion handle each side. The minimum cost across all these tests is the answer.
If we cache the results every time we compute them, we only have possible states to consider (all possible values all possible values). Each state requires time to transition (trying to break down each pillar in the range). Overall, the solution is and is given of the points.
Time Complexity:
Approach 3 — Dynamic Programming
To get full marks, we abandon recursion altogether and go for pure dynamic programming. This problem displays optimal substructure in the following way. Consider the base case where we only want to break only the first (leftmost) block. Given this value, we can then use it to calculate the cost of breaking the two leftmost pillars. Likewise, if we know the cost to break the first pillars, then we can calculate the cost to break the first pillars.
There are 2 values in our state: the current pillar we are on, and whether it is broken (either or ). stores the minimum cost to achieve this state. You can think of as the "important" values, since our goal is ultimately to knock pillars down. However, we will still need values to calculate the values along the way. Once again, represents the cost to break all the pillars from to . Every state is equal to the minimum cost of two cases:
- the pillar before it is already knocked down (i.e. ): Here, the additional cost to knock down one more pillar (the current one) is just the current pillar's durability , minus the damage done by the previous pillar .
- the pillar before it is not knocked down (i.e. ): Here, there is no collateral damage at all, so the extra cost to break one more pillar (the current one) is just its durability .
Calculating is slightly trickier. We must always assume that the pillar after it is broken. This is a valid assumption because in the next iteration, we always ensure that the assumption made in the previous iteration is fulfilled by actually assuming again that it is broken (i.e. ). In other words, the assumptions in one iteration depend on the assumptions made in the next, and the assumption in the current iteration depends on the assumptions made in the previous. An interesting form of induction indeed. Yet again, we have 2 cases to consider, of which we take the minimum cost:
- the pillar before it is already knocked down (i.e. ): Here, the additional cost to knock down one more pillar (the current one) is just the current pillar's durability , minus the damage done by the previous pillar , minus the damage done by the next pillar .
- the pillar before it is not knocked down (i.e. ): Here, there is no collateral damage at all from the previous pillar, so the extra cost to break one more pillar (the current one) is just its durability minus the damage done by the next pillar .
Time Complexity:
Comments