Split a grid into a grid

22

1

Introduction

There is a small village with nothing but a few houses and empty fields. The local bureaucrats want to divide the village into lots so that each lot contains exactly one house, and the borders of the lots form a nice straight-line grid. Your task is to determine whether this is possible.

The task

Your input is a rectangular 2D array of bits; 1 represents a house and 0 an empty field. Its size will be at least 1×1, and it will contain at least one 1. You can take the input in any reasonable format (nested list of integers, list of strings, multiline string etc).

Your program shall determine whether the array can be divided into grid cells using straight horizontal and vertical lines so that each grid cell contains exactly one 1. The grid cells may have different sizes and shapes, although they will always be rectangular. The lines must run from one edge of the array to the opposite edge.

For example, the following is a valid division of an array:

00|0010|01|1
01|0000|00|0
--+----+--+-
00|0000|00|1
01|0010|01|0
--+----+--+-
01|1000|10|1

whereas the following division is not valid, since there are grid cells with no 1s or more than one 1:

00|0010|01|1
--+----+--+-
01|0000|00|0
00|0000|00|1
01|0010|01|0
--+----+--+-
00|1000|10|1

If a valid division exists, you shall output a truthy value, and otherwise a falsy value.

Rules and scoring

You can write a full program or a function. The lowest byte count wins.

Test cases

[[1]] -> True
[[0,1],[1,0]] -> True
[[1,1],[1,0]] -> False
[[1,0,1],[0,1,0]] -> True
[[1,0],[0,1],[0,1]] -> True
[[1,0,0],[0,0,1],[0,1,1]] -> True
[[1,1,1],[1,1,1],[1,1,1]] -> True
[[1,0,1],[0,1,0],[1,0,0]] -> True
[[1,0,0],[1,0,0],[0,1,1]] -> False
[[0,0,0,0,1],[1,0,0,1,0],[0,0,0,1,0]] -> False
[[0,0,1,0,1],[0,0,0,1,0],[0,0,0,0,0]] -> True
[[1,1,0,0,0],[0,0,0,0,0],[1,0,1,0,0]] -> True
[[1,1,0,1,1],[0,1,0,1,1],[1,0,0,0,0]] -> True
[[0,0,0,0,0,0,0],[0,1,1,1,0,1,0],[0,1,0,0,1,0,0],[0,0,0,0,0,0,1],[0,0,1,0,0,0,1],[1,1,0,1,1,0,0]] -> False
[[1,1,0,0,0,0,0],[1,0,1,1,0,1,0],[0,0,0,0,1,0,0],[0,1,0,1,1,0,0],[1,0,0,0,1,1,0],[0,0,0,0,0,1,0]] -> False
[[0,1,0,1,1,1,0],[0,0,0,0,1,0,0],[0,0,0,0,0,0,0],[1,0,0,1,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,1]] -> True
[[0,1,0,0,1,0,1],[1,0,0,0,1,0,1],[0,0,1,0,1,0,1],[1,0,0,0,1,1,0],[0,0,0,1,1,1,0],[0,1,0,0,1,0,1]] -> True
[[0,1,0,0,1,0,0,1,0],[0,0,0,0,1,1,0,1,0],[1,1,0,0,1,0,0,0,0],[0,0,1,0,1,0,1,0,0],[0,0,1,0,1,0,1,0,0],[0,1,0,0,0,1,0,0,1],[0,1,0,0,0,0,1,0,0]] -> False
[[1,0,1,0,0,1,1,0,1],[0,1,1,0,0,1,1,0,1],[1,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,1,1],[0,1,1,0,1,0,1,0,1],[1,0,1,0,0,1,1,0,1]] -> True

Zgarb

Posted 2017-01-23T07:37:25.337

Reputation: 39 083

can [ [0,0,1,0,1] , [1,0,0,1,0] , [0,0,0,1,0] ] be divided into: 3X1, 2X1, 3X2, 2X1, 2X1 rectangles in this manner or not? 001|01 ---+-- 100|10 +-- 000|10 – officialaimm – 2017-01-23T09:22:52.077

4@officialaimm No, that is not valid. The grid lines must run from one side of the array all the way to the other side. – Zgarb – 2017-01-23T09:30:48.343

Proposed test case: [[1, 0, 1], [0, 1, 0], [1, 0, 0]] That was the only 3x3 matrix my new approach was failing for. – Dennis – 2017-01-26T01:03:27.117

@Dennis Thanks, added. – Zgarb – 2017-01-26T08:51:11.093

Answers

7

Pyth, 30 29 26 24 23 bytes

sm.Asmmq1ssbCkds./MC./M

Try it online.

I'm sure this will get shorter. This is O(2mn), where m and n are the width and height of the array, but completes the last two test cases in 45 seconds on my laptop on battery (i5-5200U with limited performance).

Outputs the number of solutions.

Explanation

Five-dimensional arrays are really fun to work with.</sarcasm> You are not supposed to understand how this works even with the explanation.

                    ./M    Find all partitions of each row. Now we have a list of rows,
                           each containing the ways to split the row, each containing
                           the parts of the split (3D).
                   C       Transpose. Now we have a list of ways to split the columns,
                           each containing the rows, each containing the parts of the
                           row (3D).
                ./M        Find all partitions of each row list. Now we have a list of
                           ways to split the columns, each containing the ways to split
                           the rows, each containing the bunch of rows, each containing 
                           the rows in the bunch, each containing the parts of the row
                           (6D).
               s           Combine the ways to split rows & columns into one array (5D).
 m            d            Do the following for each way to split rows & columns (4D):
     m       k                 Do the following for each bunch of rows (3D):
            C                      Transpose the array. We now have a list of column
                                   groups, each containing the row parts (3D).
      m    b                       Do the following for each column group (2D):
          s                            Combine the row parts in the column group. We now
                                       have the list of cells in this row/column group
                                       (1D).
         s                             Sum the cells.
       q1                              Check if the sum is one.
                                   We now have the list of booleans that tell if each
                                   row/column group is valid (1D).
                               We now have the 2D list of booleans that tell if each
                               row/column group in each bunch of rows is valid. (2D)
    s                          Combine the 2D list of booleans to 1D.
  .A                           Check if all values are truthy; if the split is valid.
                           We now have the validity of each split.
s                          Sum the list to get the number of valid solutions.

PurkkaKoodari

Posted 2017-01-23T07:37:25.337

Reputation: 16 699

3

Jelly, 22 bytes

ŒṖḅ1S€€=1Ȧ€
SŒṖ⁸ṁþÇ€FS

Try it online!

Dennis

Posted 2017-01-23T07:37:25.337

Reputation: 196 637

2

Haskell, 116 bytes

import Data.List
m(a:b)=[a:e|e<-m b]++[zipWith(+)a d:e|d:e<-m b];m e=[e]
d=(any$any$all$all(==1)).map(m.transpose).m

Try it online!

Roman Czyborra

Posted 2017-01-23T07:37:25.337

Reputation: 604

1This does not compile. Please delete your answer until it is fixed. There is also a lot of golfing potential, eg. renaming mergerows to m. – Laikoni – 2017-01-23T20:12:23.340

I was planning to run out of competition due to the hard-to-beat Pyth brevity and thank thee @Laikoni for spotting I likely messed up my indentation braces. – Roman Czyborra – 2017-01-23T20:46:55.563

2This incorrectly gives False on [[1,0],[0,1],[1,0]]. The problem is that a greedy collapse can get in the way of a later better collapse. – xnor – 2017-01-23T22:03:43.630

Indeed, my [[1,1],[1,0]] collapse falsely obstructs the [[1],[1],[1]] solution. Let me sleep over this or ought i delete? – Roman Czyborra – 2017-01-23T22:17:10.860

@RomanCzyborra I'd suggest deleting while you sleep over it. – xnor – 2017-01-23T22:32:20.440

Did that! Now better? – Roman Czyborra – 2017-01-24T19:37:14.353

I think you can save a byte by swapping the two cases of m and doing m x=[x] on the second one. We have a consensus that anonymous functions don't need to be bound to a name, so you can drop the d= part. Also, you should put the verification snippet q in a separate code block; I think you have miscounted your bytes. – Zgarb – 2017-01-25T08:02:30.943

Thank you for the [x] indeed. Dropping the d= would fail to compile though given that my module already depends on Data.List.transpose and its rowmerges helper m. Byte count lazily comes from head -3|wc -c and thus counts a trailing newline and already disregards the convenience test q. – Roman Czyborra – 2017-01-25T08:57:04.313

1I count 116 bytes. – Zgarb – 2017-01-25T10:58:42.203

i know, you chopped the trailing '\n' – Roman Czyborra – 2017-01-25T12:13:15.817

1

Jelly, 20 bytes

ŒṖS€€ỊȦ$ÐfZ€µ⁺€Ȧ€€FS

This is still a brute-force solution, but it's quite a bit faster than my other answer – which cannot cope with the last two test cases on TIO – and handles all test cases in ~4 seconds.

Try it online!

How it works

ŒṖS€€ỊȦ$ÐfZ€µ⁺€Ȧ€€FS  Main link. Argument: M (matrix, array of rows)

ŒṖ                    Compute all partitions, i.e., all groupings of M's rows.
  S€€                 Map sum over all individual groupings, collapsing the grouped
                      rows into a single row.
        Ðf            Filter; keep only those partially collapsed matrices for
                      which the link to the left returns a truthy value.
       $                Group the two links to the left into a monadic chain.
     Ị                    Insignificant; map 0 and 1 to 1, greater integers to 0.
      Ȧ                   All; return 1 iff the matrix contains no zeroes.
          Z€          Zip/transpose all kept matrices,
            µ         Combine all links to the left into a monadic chain.
             ⁺€       Duplicate the chain and map it over the individual results
                      from the first call. We now have all possible combinations
                      of row and column groupings (represented by the corresponding
                      matrices of collapsed rows and columns) that do not have a
                      2 anywhere. However, they still may contain zeroes.
               Ȧ€€    Map the all atom over the matrices, returning 1 only for
                      matrices that consist entirely of ones.
                  FS  Flatten and sum, counting the number of valid divisions.

Dennis

Posted 2017-01-23T07:37:25.337

Reputation: 196 637