Pesca de redes de cubos

30

Os cubos podem ser feitos de seis quadrados como lados. Mas você também pode dobrar três retângulos 2x1 ao meio e colá-los para formar um cubo. Agora, neste desafio, você obtém um conjunto de peças feitas de quadrados e precisa determinar se pode escolher peças para formar um cubo unitário. Nem todas as peças precisam ser usadas, pode haver algumas sobras.

Detalhes

As peças são fornecidas como uma sequência de dois caracteres diferentes ou uma imagem em preto e branco ou qualquer formato raster 2D conveniente. A seguir, assumo que os pixels que formam as peças são pretos e o fundo é branco.

Dois pixels que compartilham um lado são considerados pertencentes à mesma peça. As peças só podem ser dobradas ao longo das linhas de grade que separam os pixels e não podem ser cortadas. Cada lado do cubo tem o tamanho de um pixel e cada lado do cubo pode ser feito apenas de uma única camada.

A saída deve ser um truthy ou Falsey valor.

Casos de teste

A seguir, os espaços são em segundo plano e os símbolos de hash #representam as peças.

(mais a ser adicionado)

Válido

##  
 ## 
  ##

 #  
####
 #  

# # # # # # #

# ##
## #

Inválido

###
###

#  #
####

### ## ####

Execute o seguinte snippet para mais casos de teste.

PS: Esta é uma generalização deste desafio

flawr
fonte
Por que o snippet de código JS está apenas imprimindo casos de teste codificados adicionais? Por que não colocá-los no post haha?
Urna de polvo mágico
1
@carusocomputing Essa foi apenas uma medida para impedir que o post fosse desordenado.
flawr
Sempre haverá seis pixels ativados?
Wheat Wizard
Não, pode haver mais ou menos.
flawr
1
@ Blue Ah não, analisar a entrada de peças faz parte do desafio. Essa entrada simplificaria bastante isso, então eu não permitiria isso. Obrigado por perguntar!
flawr

Respostas:

7

C, 824 803 bytes

#define Z return
#define Y char*b
#define N --n
i,j,n,w,h,A,B,C,D,E,F,G,H;char c[9999],*r,*d;x(b)Y;{if(b<c||*b<35)Z;++n;*b^=1;x(b-1);x(b+1);x(b-w);x(b+w);}m(b,p,y)Y,*p;{d=b;if(!y)for(y=-1,--p;1[++p]&31;)d+=w;for(i=0;*p&31?!(*p&16>>i)||b[i]&1:0;++i>4?p+=y,b+=w,i=0:0);Z!(*p&31)?x(d),n:0;}a(b)Y;{for(j=n=0;j<w*h;++j)if(m(c+j,b,1)||m(c+j,b,0))Z n;Z 0;}f(Y){bzero(c,9999);for(h=0,b=strcpy(c,b);r=b,b=strchr(b+1,10);h++,w=b-r);for(A=2,r=1+"@_`^C@|T@^R@XO@XX`|FB@|PP@|DD@PXN@XHX@XPX`PPXL@XHHX@XLDD@XPPX`PPPXH@PXHHH@PPPPP@";*r;r+=A+=r[-1]/96)while(a(r));A=B=C=D=E=F=G=H=0;while(a("PX")||a("XH")) (n-=3)?N?N?N?0:++H:++G:++F:++C;while(a("^")||a("PPPP"))(n-=4)?N?N?0:++H:++G:++E;while(a("P"))N?N?N?N?N?N?0:++H:++G:++F:++D:++B:++A;Z H||(G&&A)||(F&&B+B+A>1)||(E&&A>1)||D>1||C>1||((D||C)*3+B*2+A>5)*(A>1||B>2||A*B);}

Nota: Inclui uma correção de bug (a entrada anterior identificou falsamente um tromino e dois dominós como formando um cubo). No código do driver TIO, há mais casos de teste e agora há um rastreador de aprovação / reprovação; os casos de teste do hexomino foram atualizados com o valor esperado de aprovação / reprovação no rótulo.

Experimente online!

... e antes de explicar isso em detalhes, vale a pena uma visão geral de alto nível.

Visão geral básica

Esse algoritmo aplica um correspondente de padrão para classificar cada poliomaino encontrado com um determinado padrão como seu subconjunto. Como os poliominoes são correspondidos, eles são "desmarcados", excluindo-os da correspondência de padrões novamente. A classificação inicial dada pelo matcher é simplesmente uma contagem do número de peças no polyomino.

O fósforo é aplicado primeiro para separar todos os polioquinós que não podem ser dobrados em um cubo; a classificação desses poliominos é descartada. A correspondência será bem-sucedida se esses poliomatinos aparecerem nos de nível superior; portanto, nos preocupamos apenas com o menor subconjunto de "desdobrável" para cada classe. Aqui são mostrados, juntamente com as codificações acolchoadas, todos esses poliaminoinos (excluindo seus reflexos verticais). A codificação usa os bits 4-0 de cada caractere para representar quadrados em cada linha:

[^C```] [XHX``] [PPPXH] [XHHX`] [PXN``] [|PP``]
 ####.   ##...   #....   ##...   #....   ###..
 ...##   .#...   #....   .#...   ##...   #....
 .....   ##...   #....   .#...   .###.   #....
 .....   .....   ##...   ##...   .....   .....
 .....   .....   .#...   .....   .....   .....
[|FB``] [XPX``] [PPXL`] [XLDD`] [XPPX`] [|DD``]
 ###..   ##...   #....   ##...   ##...   ###..
 ..##.   #....   #....   .##..   #....   ..#..
 ...#.   ##...   ##...   ..#..   #....   ..#..
 .....   .....   .##..   ..#..   ##...   .....
 .....   .....   .....   .....   .....   .....
[|T```] [^R```] [PXHHH] [XO```] [_````] [PPPPP]
 ###..   ####.   #....   ##...   #####   #....
 #.#..   #..#.   ##...   .####   .....   #....
 .....   .....   .#...   .....   .....   #....
 .....   .....   .#...   .....   .....   #....
 .....   .....   .#...   .....   .....   #....

[XX```]
 ##...
 ##...
 .....
 .....
 .....

Depois que esses poliominoes são selecionados, categorizamos os demais poliominoes em categorias relevantes. Vale a pena notar que em quase todos os casos, é possível encontrar poliominos que permanecem (aqueles dobráveis ​​em cubos) e simplesmente procurar somas de seis. Existem duas exceções:

  • Um tromino de canto e um tromino de linha não podem formar um cubo
  • Uma linha tetromino e um dominó não podem formar um cubo

Para poder acomodar essa restrição, formamos 8 categorias, de AH: A para monominos (peças solitárias), B para dominós, C para trominos de canto, D para trominos de linha, D para trominos de linha, E para tetrominos de linha, F para outros tetrominos, G para pentominos e H para hexominos. Qualquer coisa que não se enquadre em uma dessas categorias é apenas ignorada. Contar poliominoes que se enquadram em cada categoria é suficiente.

No final, apenas retornamos a veracidade ou falsidade com base em uma equação gigante e nessas tabulações.

Ungolfed com comentários

i,j,n,w,h,A,B,C,D,E,F,G,H;char c[9999],*r,*d;
x(b)char*b;{      // recursively unmarks polyomino pointed to by b.
   if(b<c||*b<35)return;
   ++n; *b^=1;    // Tabulates tiles in n as it goes.
   x(b-1);x(b+1);x(b-w);x(b+w); // left/up/down/right
}
m(b,p,y)char*b,*p;{ // pattern match area b with pattern p, direction y.
                    //   y=1 scans down; y=0 scans up.
   d=b; // d tracks a tile in the currently matched pattern for unmarking.
        // Note that all patterns are oriented to where "top-left" is a tile.
   if(!y) // ...when scanning up, move p to the end, set y to -1 to count backward,
          //    and advance d to guarantee it points to a tile (now "bottom-left")
      for(y=-1,--p;1[++p]&31;)d+=w;
   // Match the pattern
   for(i=0;*p&31?!(*p&16>>i)||b[i]&1:0;++i>4?p+=y,b+=w,i=0:0);
   return !(*p&31)   // If it matches...
          ? x(d),n   // ...unmark/count total polyomino tiles and return the count
          : 0;
}
a(b)char*b;{ // Scan for an occurrence of the pattern b.
   for(j=n=0;j<w*h;++j)
      if(m(c+j,b,1)||m(c+j,b,0)) // (short circuit) try down then up
         return n;
   return 0;
}
// This is our function.  The parameter is a string containing the entire area,
// delimited by new lines.  The algorithm assumes that this is a rectangular area.
// '#' is used for tiles; ' ' spaces.
f(char*b) {
   bzero(c,9999); // Init categories, c buffer
   for(h=0,b=strcpy(c,b);r=b,b=strchr(b+1,10);h++,w=b-r); // Find width/height
   // Unmark all polyominoes that contain unfoldable subsets.  This was
   // compacted since the last version as follows.  A tracks
   // the current pattern's length; r[-1], usually terminator for the
   // previous pattern, encodes whether the length increases; and o/c
   // the patterns were sorted by length.
   for(A=2,r=1+"@_`^C@|T@^R@XO@XX`|FB@|PP@|DD@PXN@XHX@XPX`PPXL@XHHX@XLDD@XPPX`PPPXH@PXHHH@PPPPP@";*r;r+=A+=r[-1]/96)
      while(a(r));
   A=B=C=D=E=F=G=H=0;
   // Match corner trominoes now to ensure they go into C.
   while(a("PX")||a("XH"))
      (n-=3)
         ?   --n
             ?   --n
                 ?   --n
                    ?   0 // More than 6 tiles?  Ignore it.
                    : ++H // 6 tiles?  It's an H.
                 : ++G // 5 tiles?  It's a G.
             : ++F // 4 tiles?  It's an F.
        : ++C; // only 3 tiles?  It's a C.
   // Now match line tetrominoes to ensure they go into E.
   while(a("^")||a("PPPP"))
      (n-=4)
         ?   --n
             ?   --n
                 ?   0 // More than 6 tiles?  Ignore it.
                 : ++H // 6 tiles?  It's an H.
             : ++G // 5 tiles?  It's a G.
         : ++E; // only 4 tiles?  It's an E.
   // Find all remaining tetrominoes ("P" is a monomino pattern)
   while(a("P"))
      --n
         ?   --n
             ?   --n
                 ?   --n
                     ?   --n
                         ?   --n
                             ?   0 // More than 6 tiles?  Ignore it.
                             : ++H // 6 tiles?  It's an H.
                         : ++G // 5 tiles? It's a G.
                     : ++F // 4 tiles?  It's an F.
                : ++D // 3 tiles?  It's a D.
            : ++B // 2 tiles?  It's a B.
         : ++A; // only 1 tile? It's an A.
   // Figure out if we can form a cube:
   return H               // Yes if we have a foldable hexomino
      ||(G&&A)            // Yes if we have a foldable pentomino
                          // and a monomino
      ||(F&&B+B+A>1)      // Yes if we have a foldable non-line tetromino
                          // and 2 other tiles (dominoes count twice).
                          // Either one domino or two monominoes will do.
      ||(E&&A>1)          // Yes if we have a foldable line tetromino (E)
                          // and two monominoes (A).  Note we can't make a
                          // cube with a line tetromino and a domino (B).
      ||D>1               // Yes if we have two line trominoes
      ||C>1               // Yes if we have two corner trominoes
      ||((D||C)*3+B*2+A>5)
                          // Any combination of trominoes, dominoes, monominoes>6,
                          // where trominoes are used at most once
                          // (via logical or)...
         * (A>1||B>2||A*B)
                          // ...times this includer/excluder fudge factor
                          // that culls out the one non-working case;
                          // see table:
                          //
                          // Trominos Dominos Monomos Cube  A>1 B>2 A*B
                          //    1        0       3+    yes   Y   N   0
                          //    1        1       1+    yes   Y   N   1
                          //    1        2       0     no    N   N   0
                          //    0+       3       0+    yes   Y   Y   1
      ;
}
H Walters
fonte
Isso não vai funcionar. A questão diz que algumas peças podem ser não utilizado
John Dvorak
@JanDvorak Obrigado por apontar isso!
H Walters
Para mim, parece estranho que você soletre tromino em vez de triomino , mas ambas são grafias válidas, ao que parece.
mbomb007