Łowienie sieci sześcianowych

30

Kostki mogą być wykonane z sześciu kwadratów jako boków. Ale możesz również złożyć trzy prostokąty 2x1 na pół i skleić je ze sobą, tworząc sześcian. Teraz w tym wyzwaniu otrzymujesz zestaw elementów, z których każdy składa się z kwadratów, i musisz ustalić, czy możesz wybrać elementy, aby utworzyć kostkę jednostkową. Nie wszystkie elementy muszą być użyte, mogą pozostać jakieś resztki.

Detale

Elementy są podawane jako ciąg dwóch różnych znaków lub czarno-biały obraz lub dowolny dogodny format rastrowy 2D. Poniżej zakładam, że piksele tworzące kawałki są czarne, a tło jest białe.

Uważa się, że dwa piksele dzielące bok należą do tego samego elementu. Kawałki można składać tylko wzdłuż linii siatki oddzielających piksele i nie można ich ciąć. Każda strona sześcianu ma rozmiar jednego piksela, a każda strona sześcianu może być wykonana tylko z jednej warstwy.

Dane wyjściowe muszą być zgodne z prawdą lub falsey .

Przypadki testowe

Poniżej spacje są tłem, a symbole skrótu #reprezentują elementy.

(więcej do dodania)

Ważny

##  
 ## 
  ##

 #  
####
 #  

# # # # # # #

# ##
## #

Nieważny

###
###

#  #
####

### ## ####

Uruchom następujący fragment kodu, aby uzyskać więcej przypadków testowych.

PS: To jest uogólnienie tego wyzwania

wada
źródło
Dlaczego fragment kodu JS drukuje tylko dodatkowe zakodowane testy? Dlaczego nie po prostu umieścić ich na poczcie haha?
Magic Octopus Urn
1
@carusocomputing To był tylko środek zapobiegający zaśmiecaniu postu.
flawr
Czy zawsze będzie sześć pikseli?
Wheat Wizard
Nie, może mniej więcej.
flawr
1
@Blue Ah nie, analiza wkładu na kawałki jest częścią wyzwania. Ten wkład nieco uprościłby to, więc nie pozwoliłbym na to. Dziękuję za pytanie!
flawr

Odpowiedzi:

7

C, 824 803 bajtów

#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);}

Uwaga: Zawiera poprawkę błędu (poprzedni wpis fałszywie identyfikował tromino i dwa domino jako tworzące sześcian). W kodzie sterownika TIO jest więcej przypadków testowych i teraz jest moduł śledzenia pass / fail; przypadki testowe hexomino zostały zaktualizowane o oczekiwaną wartość pozytywnej / negatywnej na etykiecie.

Wypróbuj online!

... a zanim szczegółowo to wyjaśnię, warto zapoznać się z ogólnym przeglądem.

Podstawowy przegląd

Ten algorytm stosuje dopasowywanie wzorców do klasyfikowania każdego znalezionego poliomino z danym wzorcem jako jego podzbioru. Po dopasowaniu poliomino są one „nieoznaczone”, co wyklucza ich ponowne dopasowanie do wzorca. Początkowa klasyfikacja podana przez dopasowującego jest po prostu liczbą płytek w poliomino.

Matcher jest nakładany najpierw, aby zabić wszystkie poliominoe, których nie można złożyć na sześcian; klasyfikacja tych poliominos została odrzucona. Dopasowanie zakończy się sukcesem, jeśli te poliomino pojawią się w obrębie tych wyższych; dlatego zależy nam tylko na najmniejszym podzbiorze „rozwijanego” dla każdej klasy. Pokazano tutaj wraz z wyściełanymi kodowaniami wszystkie takie poliominoesy (z wyjątkiem ich odbić pionowych). Kodowanie wykorzystuje bity 4-0 każdego znaku do reprezentowania kwadratów w każdym rzędzie:

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

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

Po unicestwieniu tych poliomino dzielimy pozostałe poliomino na odpowiednie kategorie. Warto zauważyć, że w prawie wszystkich przypadkach można po prostu znaleźć pozostające poliominoes (te składane na kostkę) i po prostu wyszukać sumy sześciu. Istnieją dwa wyjątki:

  • Narożnik i tromino liniowe nie mogą tworzyć sześcianu
  • Linia tetromino i domino nie mogą tworzyć kostki

Aby sprostać temu ograniczeniu, tworzymy 8 kategorii, od AH: A dla monomino (pojedyncze płytki), B dla domino, C dla puzonów narożnych, D dla puzonów liniowych, E dla tetromino linii, F dla innych tetromino, G dla pentominoes i H dla heksominoes. Wszystko, co nie należy do jednej z tych kategorii, jest po prostu ignorowane. Wystarczy polomino, które należą do każdej kategorii.

Na koniec zwracamy prawdę lub fałsz na podstawie gigantycznego równania i tych tabel.

Nie golfił z komentarzami

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
źródło
To nie zadziała. Pytanie mówi, że niektóre elementy mogą być nieużywane
John Dvorak
@JanDvorak Dziękujemy za zwrócenie na to uwagi!
H Walters,
Wydaje mi się dziwne, że piszesz to tromino zamiast triomino , ale wydaje się , że oba są poprawnymi pisowniami.
mbomb007