Hi,
I am trying to solve CSES Counting Towers and I'm having trouble finding what's wrong with my solution. I have described my process below, and it seems I am missing few combinations.
Let's call the number of combinations for 1xn towers a(n) and for 2xn towers b(n). Consider what the last blocks at the bottom of the tower are. We can have a 2xk block (k = 1 to n) at the end or one of the column having a 1xk block (k = 1 to n) at the end. In the first case, we will have b(n-k) variations. In the second case, the 1xk block can be on the left side and the right side. If one side has this 1xk block, then there are a(k) combinations of the other side. So our answer will be 2 * a(k). But we have double counted the combination of 2 1xk blocks. So actual answer is 2 * a(k) — 1. For each of these combinations of k blocks, there are b(n-k) variations.
Combining answers for both cases, we get b(n) = sum(k = 1 to n) 2 * a(k) * b(n-k)
. We can solve for a(k) to be = 1 << (k-1)
. Resulting in
b(n) = sum(k = 1 to n) (1 << k) * b(n-k) = 2 * b(n-1) + sum(k = 2 to n) (1 << k) b(n-k)
b(n) = 2 * b(n-1) + 2 * sum(k = 2 to n) (1 << k-1) b(n-k)
Use k = 1 + v and we find that the summation is just b(n-1)
So we get b(n) = 4 * b(n-1).
This is the wrong answer, and I am unable to find which combinations I am not counting. Please help! TIA!
Auto comment: topic has been updated by BacktrackerG (previous revision, new revision, compare).
Example combination you're not counting:
n = 4, the left side is built using a 1x3 and a 1x1 block above it and the right side built using 2 1x2 blocks
Why this happens:
Your solution chooses the height of a block (k) and then adds blocks to the other one until both sides have the same height. However, this means that it assumes there is a block ending on height k (if k = 3, it'll try to add blocks to the other side until its height is exactly 3, ignoring the case where the first block that reaches height 3 doesn't end on that height but (in the counter-case above) ends on height 4).
You can decide where the next width-2 block will be placed (let this be our new k) and use width-1 blocks to "fill" the tower up to that height. We can easily see that there are a(k) * a(k) ways to do that.
Let c(n) be the number of combinations for 2xn towers if we start with a width-2 block. We can use a 2xk (k = 1 to n) block, with b(n-k) variations.
So:
However, I'm not sure it's possible to calculate b and c efficiently enough.
I used a different approach, working height by height. Here's my code (I won't explain how it works for now, but let me know if you want me to):
(I hope my explanation is clear enough)
Hope this helps!
OMG, Thank you!! The example you gave was clear in pointing out my error. Unfortunately I did look up other solutions before I made this post hoping I'd find someone else with a similar approach, so I understood the code that you posted :/ .Thanks for the detailed response on how to fix my approach too, much appreciated.
Glad I could help! :)
BTW CSES problems really helped me understand how to use DP, so if that's what you're trying to do, I'd advise you to solve as many as possible.
PS. I think I found a way to optimise the solution I suggested above (the one based on your approach), I'll try it tomorrow and post it here if it works.
I’m doing all the cses problems in order. Will stick to it!
Here is the optimised solution:
I've actually ended up changing what b is. Now b(n) is the number of combinations for 2xn towers if we start with width-1 blocks. Also, k is the height of the remaining tower. Still, the main idea of the solution is the same.
Since a(n) = 1 << (n-1), we can see that:
Let's start with b. For each k, we have a(n-k) * a(n-k) combinations for the width-1 blocks and c(k) combinations for the rest of the tower, so sum(k = 0 to n-1) a(n-k) * a(n-k) * c(k) combinations in total.
In c, after adding a 2x(n-k) block, we have b(k) + c(k) combinations for the rest of the tower, so sum(k = 0 to n-1) b(k) + c(k) combinations in total.
These are correct as long as n-2 >= 0, so it won't work for n = 1. This will be our base case. There is only one way to split a 2x1 tower into width-1 blocks, and one way to split it into width-2 blocks, so b(1) = 1 and c(1) = 1.
Here's where it gets interesting: As you can see, this is basically the same as my previous solution. However, the idea is completely different.
Instead of b and c, we have a 2D array ans. ans[n][1] is the number of combinations if level n-1 is "split" (i.e. consisting of two width-1 blocks) and ans[n][0] the number of combinations if it's not split. If level n-1 is split, the new level (n) can either be split and merged with level n-1 in 4 ways (see grecil's comment) or not be split and merged with level n-1 in 1 way. Similarly, if level n-1 is not split, level n can either be split and merged with level n-1 in 1 way, or not split and merged with level n-1 in 2 ways.
You can see my code in the parent comment.
The states in these two solutions are actually the same. In each solution, we work level by level with two cases. In the previous solution, the two cases are the previous level either being split or not split, and this solution the cases are the current level either being split or not split. Because of this, their only difference is that in the previous solution, the n for both the base case and the value we print are smaller than the ones in this solution by 1.
This was my thought process when I solved it. If you have a split block on previous layer, you can make 5 new configurations, where 1 will be non-split and 4 will be split. If you have a non-split block on previous layer, you can make 3 new configurations, 1 split and 2 non-split. (sorry for the poor handwriting and illustration, this is the figure I originally made when I solved the problem.)
My code (in Python) -
https://github.com/Grecil/pyCSES/blob/main/Dynamic%20Programming/Counting_Towers.py
Thanks for the explanation. I had looked up others’ solutions before I made this post to check if anyone else made the same mistake as me but didn’t find any. Your diagram was clear and understandable don’t worry :)