Tampa mínima do retângulo

23

Capas retangulares

Suponha que você tenha uma matriz de bits, por exemplo, o seguinte.

1 1 0 0 0 1 1 0
1 1 1 1 0 1 1 1
0 1 1 1 0 1 1 1
1 1 0 1 1 1 1 0
1 1 0 1 1 1 0 1

Gostaríamos de encontrar uma cobertura retangular para essa matriz. É um conjunto de subconjuntos retangulares da matriz que não contêm 0s, mas juntos contêm todos os 1s. Os subconjuntos não precisam ser separados. Aqui está um exemplo de uma cobertura retangular para a matriz acima.

+----+         +----+
|1  1| 0  0  0 |1  1| 0
|    |         |    |
|  +-|-----+   |    |+-+
|1 |1| 1  1| 0 |1  1||1|
+----+     |   |    || |
   |       |   |    || |
 0 |1  1  1| 0 |1  1||1|
   +-------+   |    |+-+
+----+   +-----|-+  |
|1  1| 0 |1  1 |1| 1| 0
|    |   |     +----+
|    |   |       |   +-+
|1  1| 0 |1  1  1| 0 |1|
+----+   +-------+   +-+

O número de retângulos nesta capa é 7.

A tarefa

Sua entrada é uma matriz retangular de bits, obtida em qualquer formato razoável. Você pode assumir que ele contém pelo menos um 1. Sua saída é o número mínimo de retângulos em uma tampa retangular da matriz.

A menor contagem de bytes vence. Aplicam-se as regras padrão de .

Casos de teste

[[1]] -> 1
[[1,1]] -> 1
[[1],[1]] -> 1
[[1,0,1]] -> 2
[[1,0],[0,0]] -> 1
[[1,0],[0,1]] -> 2
[[1,0],[1,1]] -> 2
[[1,1,1],[1,0,1]] -> 3
[[0,1,0],[1,1,1],[0,1,0]] -> 2
[[1,1,1],[1,0,1],[1,1,1]] -> 4
[[1,1,0],[1,1,1],[0,1,1]] -> 2
[[1,0,1,0],[1,1,1,1],[1,0,1,0]] -> 3
[[1,1,1,0],[1,0,1,0],[1,1,1,1],[0,0,1,0]] -> 4
[[1,1,1,0],[1,0,1,0],[1,1,1,1],[0,0,1,1]] -> 5
[[1,1,1,0],[1,0,1,0],[1,1,1,1],[0,1,1,1]] -> 4
[[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]] -> 3
[[0,1,0,0],[0,1,1,1],[1,1,1,0],[0,0,1,0]] -> 4
[[0,0,1,0,0],[0,1,1,1,0],[1,1,1,1,1],[0,1,1,1,0],[0,0,1,0,0]] -> 3
Zgarb
fonte
Isso é inspirado no mapa de Karnaugh ?
1
@ThePirateBay More por complexidade de comunicação não determinística .
Zgarb
@ThePirateBay para um mapa-k, todos os retângulos devem ter duas dimensões de potência.
Sparr
@Sparr. Sim eu sei disso. Eu apenas perguntei se era talvez a inspiração para esse desafio.
1
Caso de teste útil para abordagens gananciosas [[0,1,0,0],[0,1,1,1],[1,1,1,0],[0,0,1,0]]
:,

Respostas:

6

Python 2 , 318 315 271 bytes

Mr.Xcoder, ovs e Jonathan Frech economizaram muitos bytes

p=range
def f(x,i=0,j=-1):z=len(x[0]);j+=1;i+=j/z;j%=z;return i<len(x)and(x[i][j]and-~min([f([[v,v[:j]+[2]*(r-j)+v[r:]][i<=y<=e]for y,v in enumerate(x)],i,j)for e in p(i,len(x))for r in p(j+1,z+1)if all(all(k[j:r])for k in x[i:e+1])]+[f(x,i,j)-1]*(x[i][j]>1))or f(x,i,j))

Experimente online!

Halvard Hummel
fonte
4

Geléia ,  25  24 bytes

FJ‘ṁ×⁸ẆZ€Ẇ€ẎŒPFQP$$ÐṀL€Ṃ

Experimente online! Uma solução típica de complexidade do golfe, nem se preocupa com casos de teste maiores, eles expiram (o conjunto de potência de todos os retângulos possíveis é inspecionado *)

Quão?

Forma todos os retângulos possíveis que podem ser feitos. Toma o conjunto de potência desses retângulos e os inspeciona apenas mantendo aqueles conjuntos que não contêm zeros e contêm cada um deles pelo menos uma vez.

Para obter a parte "mantendo os conjuntos que não contêm zeros e contêm cada um deles pelo menos uma vez", o código primeiro coage esses para um conjunto de números inteiros distintos maior que um, deixando os zeros como estão para que se tornem " encontrar os máximos do produto dos valores únicos ".

FJ‘ṁ×⁸ẆZ€Ẇ€ẎŒPFQP$$ÐṀL€Ṃ - Link: list of lists of ones and zeros, M
F                        - flatten M into a single list
 J                       - range of length = [1,2,3,...,len(flattened(M))]
  ‘                      - increment       = [2,3,4,...,len(flattened(M))+1]
   ṁ                     - mould like M - reshapes it just like M again
     ⁸                   - chain's left argument, M
    ×                    - multiply (vectorises) - unique integers > 1 at M's 1s and 0s at M's 0s
      Ẇ                  - non-empty sublists - i.e. row selections
       Z€                - transpose €ach
         Ẇ€              - non-empty sublists of €ach - column selections of those
           Ẏ             - tighten - a flat list of all of the rectangles
            ŒP           - power-set - all possible selections of rectangles
                   ÐṀ    - filter keep those for which the following is maximal:
                  $      -   last two links as a monad:
              F          -     flatten
                 $       -     last two links as a monad:
               Q         -       de-duplicate
                P        -       product
                     L€  - length of €ach - # of rectangles used by each full-cover
                       Ṃ - minimum

* Para uma matriz n por m , são as maneiras (n, m) = 2 ^ (T (n) × T (m)) , então ...
maneiras (3,2) = 2 ^ ((3 + 2 + 1) × (2 + 1)) = 2 ^ 18 = 262.144 (o link do TIO)
maneiras (3,3) = 2 ^ ((3 + 2 + 1) × (3 + 2 + 1)) = 2 ^ 36 = 68.719.476.736
A
soma de dois números naturais é o mesmo que o número 1. (o maior caso de teste)
maneiras (8,5) = 2 ^ 540 ~ = 3.6e + 162 (o exemplo)

Jonathan Allan
fonte
Iria FJṁ×⁸ẆZ€Ẇ€ẎŒPFQS$$ÐṀL€Ṃtrabalhar para -1? Não há tempo para testar rn.
Erik the Outgolfer
Não, porque uma capa que negligenciou (apenas) a que foi coagida 1teria o mesmo produto que uma capa válida (por exemplo, gire o exemplo de oito por cinco por meia volta e (em teoria) retornaria 6, pois negligenciaria cobrir a parte superior celular Setas Esquerda e considerá-lo válido).
Jonathan Allan
... ainda mais fácil - o caso de teste [[1,0],[0,1]]retornaria em 1vez de 2.
Jonathan Allan
1

JavaScript, 434 bytes

Código:

for(_='),d=...-1||(,Ad<=a,u[o][n]=d,    =0,(e,r,C,m))&&()=>.map((h((A,n,on<e|o<r|n>C|o>mf=u=>(q(s=(e>C[e,C]=[C,e]r>m[r,m]=[m,r]lk=1,k&=!!A)kl|=&1,=2k&lh=f=>uA,$ABf(B,$))))(($,Bae=r=C=m=,d=to-Bt=n$&n>$e   C=nn+1~ee   C=ttn-$t=oB&o>Br    m=oo+1~rr   m=tq+=sg=[],h((ca||g.push(c)gigb,j(p=1,q+=i<j&&s(b)q)';G=/[-]/.exec(_);)with(_.split(G))_=join(shift());eval(_)

Hexdump (devido a caracteres não imprimíveis):

66 6F 72 28 5F 3D 27 29 2C 13 13 64 3D 12 2E 2E 2E 11 2D 31 10 7C 7C 28 0F 2C 41 0F 64 3C 3D 0E 61 2C 0C 75 5B 6F 5D 5B 6E 5D 0B 3D 64 2C 09 3D 30 2C 08 28 65 2C 72 2C 43 2C 6D 07 29 29 13 06 26 26 28 05 29 3D 3E 04 2E 6D 61 70 28 28 03 68 28 28 41 2C 6E 2C 6F 04 02 02 6E 3C 65 7C 6F 3C 72 7C 6E 3E 43 7C 6F 3E 6D 0F 01 66 3D 75 3D 3E 28 71 08 28 73 3D 07 04 28 65 3E 43 05 5B 65 2C 43 5D 3D 5B 43 2C 65 5D 13 72 3E 6D 05 5B 72 2C 6D 5D 3D 5B 6D 2C 72 5D 13 6C 08 6B 3D 31 2C 01 6B 26 3D 21 21 41 29 13 6B 05 01 6C 7C 3D 0B 26 31 2C 0B 3D 32 06 6B 26 6C 13 68 3D 66 3D 3E 75 03 41 2C 24 04 41 03 0C 42 04 66 28 0C 42 2C 24 29 29 29 29 28 28 0C 24 2C 42 04 61 10 0F 65 3D 72 3D 43 3D 6D 3D 10 2C 64 3D 74 08 02 6F 2D 42 0F 74 3D 6E 0E 24 26 6E 3E 24 05 65 09 43 3D 6E 10 12 6E 2B 31 06 7E 65 0F 65 09 43 3D 74 12 74 08 02 6E 2D 24 0F 74 3D 6F 0E 42 26 6F 3E 42 05 72 09 6D 3D 6F 10 12 6F 2B 31 06 7E 72 0F 72 09 6D 3D 74 13 71 2B 3D 73 07 06 67 3D 5B 5D 2C 68 28 28 0C 11 63 04 61 10 7C 7C 67 2E 70 75 73 68 28 63 29 13 67 03 0C 69 04 67 03 62 2C 6A 04 28 70 3D 31 2C 71 2B 3D 69 3C 6A 26 26 73 28 11 0C 11 62 29 06 71 29 27 3B 47 3D 2F 5B 01 2D 13 5D 2F 2E 65 78 65 63 28 5F 29 3B 29 77 69 74 68 28 5F 2E 73 70 6C 69 74 28 47 29 29 5F 3D 6A 6F 69 6E 28 73 68 69 66 74 28 29 29 3B 65 76 61 6C 28 5F 29

Experimente online!

Não é muito golfe, mas pelo menos funciona muito rápido. Todos os casos de teste podem ser calculados em alguns milissegundos.

Ungolfed

f=mat=>(
  iterate=f=>mat.map((A,x)=>A.map((a,y)=>f(a,y,x))),
  fill=(x1,y1,x2,y2)=>(
    x1>x2&&([x1,x2]=[x2,x1]),
    y1>y2&&([y1,y2]=[y2,y1]),
    isFilled=0,

    canBeFilled=1,
    iterate((A,X,Y)=>X<x1|Y<y1|X>x2|Y>y2||(
      canBeFilled&=!!A
    )),

    canBeFilled&&(
      iterate((A,X,Y)=>X<x1|Y<y1|X>x2|Y>y2||(
        isFilled|=mat[Y][X]&1,
        mat[Y][X]=2
      ))
    ),

    canBeFilled&isFilled
  ),

  rectangles=0,

  iterate((a,x,y)=>a-1||(
    x1=y1=x2=y2=-1,

    start=end=0,
    iterate((A,X,Y)=>Y-y||(
      end=X,
      A||(
        start<=x&X>x&&(x1=start,x2=X-1),
        start=X+1
      )
    )),
    ~x1||(x1=start,x2=end),

    start=end=0,
    iterate((A,X,Y)=>X-x||(
      end=Y,
      A||(
        start<=y&Y>y&&(y1=start,y2=Y-1),
        start=Y+1
      )
    )),
    ~y1||(y1=start,y2=end),

    rectangles+=fill(x1,y1,x2,y2)
  )),


  ones=[],
  iterate((a,...c)=>a-1||ones.push(c)),
  ones.map((a,i)=>ones.map((b,j)=>(
    M=1,
    rectangles+=i<j&&fill(...a,...b)
  ))),

  rectangles
)

Explicação

Ele usa algoritmo semelhante ao da resolução de mapas de Karnaugh. Primeiramente, ele tenta encontrar pelo menos um 1que possa fazer parte de exatamente um retângulo não extensível. Por não extensível, quero dizer, se o estendermos em qualquer direção (para cima, esquerda, direita, baixo), ele incluirá pelo menos um 0(ou irá além dos limites da matriz). Quando isso 1for encontrado, encontre o maior retângulo que o inclui e sinalize todos os 1s nesse retângulo. Repita até que não haja mais 1s não sinalizados que atendam a essas condições.

Em alguns casos (como no 16º caso de teste), existem 1s que sobraram após a aplicação da primeira etapa. Enumere todos estes se 1aplique algum tipo de pesquisa aprimorada de força bruta. Essa etapa raramente é aplicada e, mesmo nesses casos, precisamos verificar a força bruta apenas uma ou duas combinações, para que funcione muito rápido, mesmo em casos de teste maiores.


fonte