Project 502: Castle Polyominos
In Problem 502, we are presented with the following problem: given a lattice grid of width W and height H, we wish to place blocks down on the grid, according to certain rules, such that we construct a castle.
Project Euler problem: https://projecteuler.net/problem=502
Overview of Solution Approach
Don't Count: Generate
Most of the discussions and/or attempts I've seen at the problem are attempting to actually enumerate the total number of combinations. But, think for just a minute about how insane that idea is. The number of configurations for a castle with a width and height on the order of 10 is given in the original problem as . The width and height of just one of the three problems is a TRILLION, so just constructing and storing one single castle is going to take you oodles of memory and a solid minute of computation time. The larger castle configuration counts are MOD A BILLION, which means they're so big that you have to wrap back around when you get to a billion, which means they're going to be mind-bogglingly large.
So, manual enumeration of castles is completely out.
Instead of counting castles, you should generate castles. Specifically, you should generate them using generating functions. This is where a brief dip into the mathematical literature can help. It also helps to remember that when you start talking about numbers and functions that get as big as combinatorics and permutations numbers do, you really have to utilize more sophisticated mathematical tools than manual enumeration.
That being said, implementing a computer program to enumerate castles manually (for small widths/heights) can help crystallize ideas on how to enumerate castles mathematically.
The rules established in Problem 502, repeated here again:
- Rule 1 - Blocks can be placed on top of other blocks as long as nothing sticks out past the edges or hangs out over open space.
- Rule 3 - Any two neighboring blocks on the same row have at least one unit of space between them.
- Rule 4 - The bottom row is occupied by a block of length w.
- Rule 5 - The maximum achieved height of the entire castle is exactly h.
- Rule 6 - The castle is made from an even number of blocks.
Before we can count castles, we have to find a way to represent them, and translate the rules for castles into rules for constructing representations.
There are a lot of different approaches to representing castles, but we cover a few below. The method that ended up being the most helpful was the step-based strings approach, writing castles as strings of up, down, and right steps.
We cover three approaches:
- Binary strings
- Integer tuples
- Step-based strings
Binary String Representation
One way to represent a castle is to use a binary string. Specifically, a column-wise representation of the castle can be written as a binary string, with each column consisting of two blocks: a block of zeros (indicating no blocks) and ones (indicating blocks). This will create a set of W blocks, each block of length H.
Let's look at an example:
This castle can be represented as:
11000 11100 11111 11000 11100 10000 11111 11110
The rules for constructing castles can be translated likewise into restrictions on the binary numbers we are generating. For example, the restriction that castles of height H must have at least one column of height H is a restriction on the binary digits that there must be at least one contiguous block of H 1's.
The rule that the bottom row is one continuous block, on which the rest of the castle is built, is equivalent to saying that each block begins with a 1.
The rule that the number of blocks must be even requires a little more care, but we can count the number of blocks as follows:
Split the entire string into blocks of size H:
11000 11100 11111 11000 11100 10000 11111 11110
Now, for each block, count the number of ones in that block:
2 3 5 2 3 1 5 4
Add a zero to the front and end:
0 2 3 5 2 3 1 5 4 0
Now compute the difference between each entry (entry i minus entry i-1):
+2 +1 +2 -3 +1 -2 +4 -1 -4
If we sum all of the negative integers, we get the total number of blocks:
From the bit of analysis above, we might find an integer tuple representation of a castle more convenient. Specifically, we can represent a castle of known width by writing the number of up or down steps, or the height, between each rightward step.
This castle of width 8 can be represented with 8 integers:
2 3 5 2 3 1 5 4
We already saw that the number of blocks can be obtained by summing the negative numbers, and this can be used to check if the number of blocks is even. We can also easily specify that the castle should reach a height of exactly H by ensuring there is at least one integer with the value of H in the tuple, and ensure that the castle does not drop below the minimum height by ensuring all integers in the tuple are > 0.
The representation that led to the most breakthroughs in progressing with PE 502 was the same representation used to solve the Lattice Paths problem in Project Euler/15. In that problem, we represented a path through a lattice using a string of letters like
RRRDDD. We can do the same thing here to represent castles.
Let's start with the simplest castles: rectangular castles. These castles consist of Us, then Rs, then Ds, representing upward steps, rightward steps, and downward steps. The 4 x 8 rectangular castle would be:
The next type of castle is what we will call a convex castle. This type of castle can be split into three portions: the front, the middle, and the back.
- The front of the castle consists of interspersed U and R steps, until the castle reaches its maximum height. No D steps are present in the front part.
- The middle of the castle consists of any R steps taken at the maximum height. There must be at least one R step in the middle.
- The back of the castle consists of interspersed D and R steps, until the castle reaches its base. No U steps are present in the back part.
- The convex castle must begin with a U and end with a D (as per Rule X).
A few examples of convex castles:
UURUURUURUU RRRRRRR DDRDDRDDRDD ^ ^ ^ front middle back URRRRRRUUUUURDDDDDD UURRUURRRRRRUURDRDRRDDDD UUUUUURRRRUUUUUURURRRDDDDDDRRDRRRRDRRDRRRRRRDDDD URRRRUUURRRRRUUUURUUUUUURUUURRRUUURRRRRRUUUURRRRRURRRRRUURRUURRRRRURRURRURRRRRRDDDDDRRRRRRRDDDRDRDRDRDRDRDRRRRDDDRRDDDRRRDDDDDDDDDDDRRRRRRRRD
Finally, we can classify all remaining castles as variations on convex castles. To construct variations on castles, we look for runs of RRRs of length 3 or more. Each pair of Rs creates a slot for additional U or D moves, with long runs of Rs creating many slots and many possible castle variations.
For example, suppose we had a castle that began like this:
This string of Rs creates places where the castle could go. Assuming the maximum height the castle can reach is 6, we can construct variations by inserting Ds and Us in-between pairs of Rs. Note that we MUST insert Ds first; if we insert Us first, we will construct a duplicate castle variation that would be covered by another convex castle. We must also insert Ds and Us in pairs, so as to keep the total height change 0. Last, we cannot insert Ds and Us next to one another. Here are a few example castle variations:
UUUURDRURDRURRUU UUUURDDRDRUURURRUU UUUURDRUURDRRRUU UUUURDRURURDRRUU UUUURDRUURRRDRUU UUUURRDDRUUURRDRUU
and so on...
In the next section we will show how to enumerate all convex castles, and given a convex castle, how to enumerate all variations. This will allow us to count the number of castles.
One last convenient property of this representation is the fact that each D move completes a new block, so checking if there are an even number of blocks is as easy as checking if the number of Ds is even.
Let's talk through some formulas for counting castles.
File: step polyominos, motzkin numbers, and bessel functions: File:PolyominosMotzkinBessel.pdf
File: a method for the enumeration of classes of column-convex polygons File:EnumerationColumnConvexPolynomials.pdf
File: construction procedure for parallel polyomino transfer matrices File:PolyominoTransferMatrix.pdf
Project Eulerproject euler notes
Round 1: Problems 1-20
Round 2: Problems 50-70
Round 3: Problems 100-110
Round 4: Problems 500-510
Round 5: Problems 150-160
Round 6: Problems 250-260
Round 7: Problems 170-180Flags · Template:ProjectEulerFlag · e