9

You are given a set of blocks 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.

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 to build a 48”×10” panel?

This is what I understand so far:

with the blocks 3 x 1 and 4.5 x 1

I've used combination formula to find all possible combinations that the 2 blocks can be arranged in a panel of this size

C = choose --> C(n, k) = n!/r!(n-r)! combination of group n at r at a time

Panel: 7.5 x 1 = 2 ways -->

1 (3 x 1 block) and 1 (4.5 x 1 block) --> Only 2 blocks are used--> 2 C 1 = 2 ways

Panel: 7.5 x 2 = 2 ways

I used combination here as well

1(3 x 1 block) and 1 (4.5 x 1 block) --> 2 C 1 = 2 ways

Panel: 12 x 3 panel = 2 ways -->

2(4.5 x 1 block) and 1(3 x 1 block) --> 3 C 1 = 3 ways

0(4.5 x 1 block) and 4(3 x 1 block) --> 4 C 0 = 1 way

3 ways + 1 way = 4 ways

(This is where I get confused)

Panel 27 x 5 panel = 7958 ways

6(4.5 x 1 block) and 0(3 x 1) --> 6 C 0 = 1 way

4(4.5 x 1 block) and 3(3 x 1 block) --> 7 C 3 = 35 ways

2(4.5 x 1 block) and 6(3 x 1 block) --> 8 C 2 = 28 ways

0(4.5 x 1 block) and 9(3 x 1 block) --> 9 C 0 = 1 way

1 way + 35 ways + 28 ways + 1 way = 65 ways

As you can see here the number of ways is nowhere near 7958. What am I doing wrong here?

Also how would I find how many ways there are to construct a 48 x 10 panel? Because it's a little difficult to do it by hand especially when trying to find 7958 ways.

How would write a program to calculate an answer for the number of ways for a 7958 panel? Would it be easier to construct a program to calculate the result? Any help would be greatly appreciated.

Community
  • 1
  • 1
MK1
  • 447
  • 1
  • 5
  • 9
  • 6
    6 upvotes? you gotta be kidding! Very dodgy... – Mitch Wheat Jul 11 '11 at 03:28
  • 1
    @Mitch, except this time, the asker has actually shown some effort. They haven't written any code yet because they don't understand the problem statement, so they're asking for help in understanding it. – Tyler Jul 11 '11 at 03:32
  • 1
    OP, it looks like you are only considering a single row at a time, hence the relatively small numbers you are getting. It just happens that for some of the smaller examples, choosing the layout of the first row uniquely determines the layout of the row above (by the spacing property), so you are getting the correct numbers for those. –  Jul 11 '11 at 03:33
  • 2
    @Matrixfrog: OK, but 6 upvotes? – Mitch Wheat Jul 11 '11 at 03:33
  • 2
    I'm not sure why the "choose" function is right for this problem, but assuming that it is, I think your calculation is the number of ways to make a 27x1 panel. Then you can make the second layer in 65 ways, so there are 65^2 ways to make a 27x2 panel. *Except* some of them are not going to work, because they'll have the separation between bricks lined up with each other. – Tyler Jul 11 '11 at 03:35
  • 1
    @Mitch I think it's an interesting question, and it's language-agnostic (even though it's not tagged as such) which means it could potentially appeal to anyone, unlike most questions which only appeal to, say, Java people or Python people or whatever. *shrug* – Tyler Jul 11 '11 at 03:36

5 Answers5

4

I don't think the "choose" function is directly applicable, given your "the spaces between the blocks must not line up in adjacent rows" requirement. I also think this is where your analysis starts breaking down:

Panel: 12 x 3 panel = 2 ways -->

2(4.5 x 1 block) and 1(3 x 1 block) --> 3 C 1 = 3 ways

0(4.5 x 1 block) and 4(3 x 1 block) --> 4 C 0 = 1 way

3 ways + 1 way = 4 ways

...let's build some panels (1 | = 1 row, 2 -'s = 1 column):

+---------------------------+
|      |      |      |      |
|      |      |      |      |
|      |      |      |      |
+---------------------------+

+---------------------------+
|          |         |      |      
|          |         |      |      
|          |         |      |      
+---------------------------+

+---------------------------+
|      |          |         |            
|      |          |         |      
|      |          |         |      
+---------------------------+

+---------------------------+
|          |      |         |                  
|          |      |         |      
|          |      |         |      
+---------------------------+

Here we see that there are 4 different basic row types, but none of these are valid panels (they all violate the "blocks must not line up" rule). But we can use these row types to create several panels:

+---------------------------+
|      |      |      |      |
|      |      |      |      |
|          |         |      |      
+---------------------------+

+---------------------------+
|      |      |      |      |
|      |      |      |      |
|      |          |         |      
+---------------------------+

+---------------------------+
|      |      |      |      |
|      |      |      |      |
|          |      |         |     
+---------------------------+

+---------------------------+
|          |         |      |      
|          |         |      |
|      |      |      |      |
+---------------------------+

+---------------------------+
|          |         |      |      
|          |         |      |
|      |          |         |
+---------------------------+

+---------------------------+
|          |         |      |      
|          |         |      |
|          |      |         |
+---------------------------+

...

But again, none of these are valid. The valid 12x3 panels are:

+---------------------------+
|      |      |      |      | 
|          |      |         |
|      |      |      |      |
+---------------------------+

+---------------------------+
|          |      |         |
|      |      |      |      |
|          |      |         |
+---------------------------+

+---------------------------+
|          |         |      |
|      |          |         |
|          |         |      |
+---------------------------+

+---------------------------+
|      |          |         |
|          |         |      |
|      |          |         |
+---------------------------+

So there are in fact 4 of them, but in this case it's just a coincidence that it matches up with what you got using the "choose" function. In terms of total panel configurations, there are quite more than 4.

aroth
  • 54,026
  • 20
  • 135
  • 176
3
  1. Find all ways to form a single row of the given width. I call this a "row type". Example 12x3: There are 4 row types of width 12: (3 3 3 3), (4.5 4.5 3), (4.5 3 4.5), (3 4.5 4.5). I would represent these as a list of the gaps. Example: (3 6 9), (4.5 9), (4.5 7.5), (3 7.5).

  2. For each of these row types, find which other row types could fit on top of it.

    Example:

    a. On (3 6 9) fits (4.5 7.5).

    b. On (4.5 9) fits (3 7.5).

    c: On (4.5 7.5) fits (3 6 9).

    d: On (3 7.5) fits (4.5 9).

  3. Enumerate the ways to build stacks of the given height from these rules. Dynamic programming is applicable to this, as at each level, you only need the last row type and the number of ways to get there.

Edit: I just tried this out on my coffee break, and it works. The solution for 48x10 has 15 decimal digits, by the way.

Edit: Here is more detail of the dynamic programming part:

Your rules from step 2 translate to an array of possible neighbours. Each element of the array corresponds to a row type, and holds that row type's possible neighbouring row types' indices.

0: (2)
1: (3)
2: (0)
3: (1)

In the case of 12×3, each row type has only a single possible neighbouring row type, but in general, it can be more.

The dynamic programming starts with a single row, where each row type has exactly one way of appearing:

1 1 1 1

Then, the next row is formed by adding for each row type the number of ways that possible neighbours could have formed on the previous row. In the case of a width of 12, the result is 1 1 1 1 again. At the end, just sum up the last row.

Complexity:

  • Finding the row types corresponds to enumerating the leaves of a tree; there are about (/ width 3) levels in this tree, so this takes a time of O(2w/3) = O(2w).

  • Checking whether two row types fit takes time proportional to their length, O(w/3). Building the cross table is proportional to the square of the number of row types. This makes step 2 O(w/3·22w/3) = O(2w).

  • The dynamic programming takes height times the number of row types times the average number of neighbours (which I estimate to be logarithmic to the number of row types), O(h·2w/3·w/3) = O(2w).

As you see, this is all dominated by the number of row types, which grow exponentially with the width. Fortunately, the constant factors are rather low, so that 48×10 can be solved in a few seconds.

Community
  • 1
  • 1
Svante
  • 50,694
  • 11
  • 78
  • 122
1

This looks like the type of problem you could solve recursively. Here's a brief outline of an algorithm you could use, with a recursive method that accepts the previous layer and the number of remaining layers as arguments:

  • Start with the initial number of layers (e.g. 27x5 starts with remainingLayers = 5) and an empty previous layer
  • Test all possible layouts of the current layer
    • Try adding a 3x1 in the next available slot in the layer we are building. Check that (a) it doesn't go past the target width (e.g. doesn't go past 27 width in a 27x5) and (b) it doesn't violate the spacing condition given the previous layer
    • Keep trying to add 3x1s to the current layer until we have built a valid layer that is exactly (e.g.) 27 units wide
    • If we cannot use a 3x1 in the current slot, remove it and replace with a 4.5x1
    • Once we have a valid layer, decrement remainingLayers and pass it back into our recursive algorithm along with the layer we have just constructed
  • Once we reach remainingLayers = 0, we have constructed a valid panel, so increment our counter

The idea is that we build all possible combinations of valid layers. Once we have (in the 27x5 example) 5 valid layers on top of each other, we have constructed a complete valid panel. So the algorithm should find (and thus count) every possible valid panel exactly once.

  • Sounds like it would make a good Prolog program... – Tyler Jul 11 '11 at 03:56
  • @HappyPixel i understand the concept of what your answer entails, and it definitely seems like this is a great solution. The recursive that accepts the 2 layers is a great idea. But could you illuminate on this more. Could you give some pseudo code so I could discern the concept more clearer? How would you go about constructing these layers? – MK1 Jul 11 '11 at 04:14
  • @MK1 I'm writing some sample Java code for this, I'll post it when it's working –  Jul 11 '11 at 04:24
  • @MK1 posted another answer with the Java code, seems to give the correct answer for 27x5 –  Jul 11 '11 at 04:37
  • @HappyPixel this definitely makes it a whole lot clearer. Thanks. – MK1 Jul 11 '11 at 04:42
1

This is a '2d bin packing' problem. Someone with decent mathematical knowledge will be able to help or you could try a book on computational algorithms. It is known as a "combinatorial NP-hard problem". I don't know what that means but the "hard" part grabs my attention :)

I have had a look at steel cutting prgrams and they mostly use a best guess. In this case though 2 x 4.5" stacked vertically can accommodate 3 x 3" inch stacked horizontally. You could possibly get away with no waste. Gets rather tricky when you have to figure out the best solution --- the one with minimal waste.

Eben Roux
  • 12,983
  • 2
  • 27
  • 48
0

Here's a solution in Java, some of the array length checking etc is a little messy but I'm sure you can refine it pretty easily.

In any case, I hope this helps demonstrate how the algorithm works :-)

import java.util.Arrays;

public class Puzzle
{
    // Initial solve call
    public static int solve(int width, int height)
    {
        // Double the widths so we can use integers (6x1 and 9x1)
        int[] prev = {-1};      // Make sure we don't get any collisions on the first layer
        return solve(prev, new int[0], width * 2, height);
    }

    // Build the current layer recursively given the previous layer and the current layer
    private static int solve(int[] prev, int[] current, int width, int remaining)
    {
        // Check whether we have a valid frame
        if(remaining == 0)
            return 1;

        if(current.length > 0)
        {
            // Check for overflows
            if(current[current.length - 1] > width)
                return 0;

            // Check for aligned gaps
            for(int i = 0; i < prev.length; i++)
                if(prev[i] < width)
                    if(current[current.length - 1] == prev[i])
                        return 0;

            // If we have a complete valid layer
            if(current[current.length - 1] == width)
                return solve(current, new int[0], width, remaining - 1);
        }

        // Try adding a 6x1
        int total = 0;
        int[] newCurrent = Arrays.copyOf(current, current.length + 1);
        if(current.length > 0)
            newCurrent[newCurrent.length - 1] = current[current.length - 1] + 6;
        else
            newCurrent[0] = 6;
        total += solve(prev, newCurrent, width, remaining);

        // Try adding a 9x1
        if(current.length > 0)
            newCurrent[newCurrent.length - 1] = current[current.length - 1] + 9;
        else
            newCurrent[0] = 9;
        total += solve(prev, newCurrent, width, remaining);

        return total;
    }

    // Main method
    public static void main(String[] args)
    {
        // e.g. 27x5, outputs 7958
        System.out.println(Puzzle.solve(27, 5));
    }
}
  • So, you are proposing the more-or-less brute force method of trying all combinations, breaking off the branches as soon as a mismatch occurs, right? – Svante Jul 11 '11 at 06:31
  • @Svante Yeah, pretty much. In each call of solve, it goes as far as it can along the "3x1" branch, then backtracks if it gets stuck. It then does the same for the "4.5x1" branch. I can't think of any reasonable way to solve this problem combinatorially because the gap condition makes it pretty complicated. –  Jul 11 '11 at 06:57
  • How long does this take for the 48x10 panel? – Svante Jul 11 '11 at 07:05
  • @Svante Good question. I started running it for 48x10 earlier, forgot about it for a bit and it was still running when I came back to it a good while later, lol. Fwiw, the 27x5 takes about 180ms on my laptop so you can expect the 48x10 to take ages. I'd _guess_ the complexity is something like O(c^dmn) for an m x n panel and some constants c, d. Just a guess though, no idea how to prove it. –  Jul 11 '11 at 07:14
  • Well, it seems that the puzzle is about finding a better method, then. – Svante Jul 11 '11 at 09:40
  • 2
    Why was I downvoted? I gave a perfectly valid answer to the OP's question... I never claimed my method is super efficient or anything, I was just demonstrating a recursive solution to the problem. –  Jul 11 '11 at 09:55
  • Another problem is that it does not work at all for 48x10, as the solution has 15 decimal digits. Even if you wait for the aeons it takes, the result will be wrong due to silent overflow. – Svante Jul 11 '11 at 13:20
  • 1
    Again, it's just for demonstration purposes (to illustrate the algorithm I described in my other answer). I could have written it in pseudocode, I just find it easier to write Java. I don't see why you're so keen to pick holes in this solution when it's just meant to be a quick-and-dirty demonstration... the OP even said he found it helpful if you see the comments on my other answer. –  Jul 11 '11 at 13:48
  • @HappyPixel When trying to determine the number of ways for a panel of 27 x 5 i get 7958. I then realized that the number of ways is only displayed for panels up to 48 x 4 in a reasonable time, if any width beyond 4 is entered the program doesn't return a value or is still processing the data. Any ideas on how to fix this? Would the program have to be condensed into one method, is it possible? I've been breaking my brain trying to figure this out. I know there's a way to do this. It seems like were so close. – MK1 Jul 11 '11 at 21:04
  • 1
    @Svante - So then why not post the code for your working solution so that we might all learn from it? – aroth Jul 11 '11 at 22:31
  • @aroth: I outlined my solution in my own answer (http://stackoverflow.com/questions/6645464/programming-puzzle-question-can-anyone-out-there-help-me/6646772#6646772). My implementation is in Common Lisp, though, so it does not fit the C# nor the Java tag. – Svante Jul 12 '11 at 06:39