I was given this question to test my programming ability. Though I feel it is as much math as it is programming. I have failed honestly, but I would like to know how it is done for future reference.
The ideal answer will use recursion and threads.
Your niece was given a set of blocks for her birthday, and she has decided to build a panel using 3”×1” and 4.5”×1″ blocks. For structural integrity, the spaces between the blocks must not line up in adjacent rows. For example, the 13.5”×3” panel below is unacceptable, because some of the spaces between the blocks in the first two rows line up (as indicated by the dotted line).
There are 2 ways in which to build a 7.5”×1” panel, 2 ways to build a 7.5”×2” panel, 4 ways to build a 12”×3” panel, and 7958 ways to build a 27”×5” panel. How many different ways are there for your niece to build a 48”×10” panel? The answer will fit in a 64-bit integer. Write a program to calculate the answer.
That is the issue, I am not sure where to begin in terms of math. I understand I need to compute all possible combinations for the first row. But I am not really sure how. Then you could at this point on a particular thread calculate all possible combos for the next row and so on. And then each first row combination could get its own thread and pass its setup into a recursive algorithm that compares the current row to the last one, and finds a possible answer. But I can’t really program it because I don’t know how to calculate the possible combinations for any row. If I did, then maybe I could check to see if its a legal row (no two blocks exactly on top of each other (staggered)) and move on to the next one. Possibly each row would be a for loop for nested around the next one in terms of Code. But again I don’t know what to do for the mathematical aspect of it.
I think you have a good initial grasp of the problem. If I understand your thoughts correctly, you ought to be recursing on each block placement, not on each row placement – as blocks that are oriented vertically will quickly preclude any ordinary rows.
Here’s the approach I would take: you will end up building a tree (either explicitly in memory or implicit: the tree can be an argument that is passed in your recursive function). The node of the tree will be a state of the tree – so the root is “no blocks placed”. You place a first block “somehow” (we’ll get to that) and that represents a new state.
The goal will be to construct a set of leaf nodes which are all complete (the board is filled in) and legal (no cracks line up). So how do we get there?
For each block placement, there are 4 “alternate realities”: a 3×1 block placed horizontally, a 3×1 placed vertically (call this 1×3), a 4.5×1 placed horizontally, and a 1×4.5. For each of these options, you will attempt to “legally” place the block at the next point in the row. If it is legal (legal being someting along the lines of “block doesn’t overlap the board edges, block doesn’t share a vertical edge”) then you can accept that board as an intermediate state and recurse with that new state. If it is not legal, then that state must be abandoned.
Thinking this way, the first 4 children nodes will be a block in the bottom-left corner as [3×1, 1×3, 4.5×1, 1×4.5]. Each of those four states will have a block “just to the right” in one of 4 configurations, and so on.
In order to move across the rows, when you hit the right edge, I would find the “lowest” empty spaces and fill them arbitrarily when they tie from left to right. This is eager enough to prune large sets when the edges are ragged, but still works well when the levels are flat, like when you’re first starting out.
In essence, your tree will have (up to) 4 nodes for each intermediate state, with the edges representing an “attempted placement”. If you cannot legally place the block at that “attempted placement”, you do not recurse, pruning that possibility and all descendents from the tree.
This brute-force method should get you a completed board, even if its computational complexity is astronomical. It is only at the point that you can correctly solve some problems that you should consider parallelizing with threads. Recursive problems tend to lend themselves well to threads, as each recursion can often be parallelized without too much pain. Just make sure you get it right first.