Zadanie
Napisz program, który odczytuje trzy liczby całkowite m , n albo ze STDIN, albo jako argumenty wiersza poleceń, drukuje wszystkie możliwe nachylenia prostokąta o wymiarach m × n przez domino 2 × 1 i 1 × 2, a na koniec liczbę prawidłowych przechyleń.
Domeny poszczególnych kafelków muszą być reprezentowane przez dwie kreski ( -
) dla 2 × 1 i dwa pionowe słupki ( |
) dla 1 × 2 dominów. Po każdym kafelku (w tym ostatnim) musi następować przesuw linii.
Do celów punktacji musisz również zaakceptować flagę ze STDIN lub jako argument wiersza poleceń, który powoduje, że Twój program wypisuje tylko liczbę prawidłowych nachyleń, ale nie same nachylenia.
Twój program nie może być dłuższy niż 1024 bajty. Musi działać dla wszystkich wejść, tak że m × n ≤ 64 .
(Zainspirowany przez Wydrukuj wszystkie domina z prostokątem 4x6 .)
Przykład
$ sdt 4 2
----
----
||--
||--
|--|
|--|
--||
--||
||||
||||
5
$ sdt 4 2 scoring
5
Punktacja
Twój wynik zależy od czasu wykonania programu dla wejścia 8 8 z ustawioną flagą.
Aby ten kod był najszybszym, a nie najszybszym wyzwaniem komputerowym , uruchomię wszystkie zgłoszenia na własnym komputerze (Intel Core i7-3770, 16 GiB PC3-12800 RAM), aby ustalić oficjalny wynik.
Proszę zostawić szczegółowe instrukcje, jak skompilować i / lub wykonać swój kod. Jeśli potrzebujesz konkretnej wersji kompilatora / tłumacza swojego języka, złóż oświadczenie w tej sprawie.
Zastrzegam sobie prawo do pozostawienia zgłoszeń bez oceny, jeśli:
Nie ma darmowego (jak w piwie) kompilatora / interpretera dla mojego systemu operacyjnego (Fedora 21, 64 bity).
Pomimo naszych starań Twój kod nie działa i / lub generuje niepoprawne dane wyjściowe na moim komputerze.
Kompilacja lub wykonanie trwa dłużej niż godzinę.
Twój kod lub jedyny dostępny kompilator / interpreter zawiera wywołanie systemowe
rm -rf ~
lub coś równie podejrzanego.
Tabela liderów
Ponownie oceniłem wszystkie zgłoszenia, uruchamiając zarówno kompilacje, jak i wykonania w pętli z 10 000 iteracji do kompilacji i od 100 do 10 000 iteracji do wykonania (w zależności od szybkości kodu) i obliczając średnią.
Oto wyniki:
User Compiler Score Approach
jimmy23013 GCC (-O0) 46.11 ms = 1.46 ms + 44.65 ms O(m*n*2^n) algorithm.
steveverrill GCC (-O0) 51.76 ms = 5.09 ms + 46.67 ms Enumeration over 8 x 4.
jimmy23013 GCC (-O1) 208.99 ms = 150.18 ms + 58.81 ms Enumeration over 8 x 8.
Reto Koradi GCC (-O2) 271.38 ms = 214.85 ms + 56.53 ms Enumeration over 8 x 8.
źródło
--
. Jeśli jest pionowy, jest dwa|
, jeden pod drugim.Odpowiedzi:
do
Prosta implementacja ...
Wersja oszukująca
Wyjaśnienie szybszego algorytmu
Skanuje od lewej do prawej i utrzymuje stan
d[i][j]
, w którym:i
jest w[0,m)
, co oznacza bieżącą kolumnę.j
jest bitowym wektorem wielkościn
, gdzie bit wynosiłby 1, jeśli odpowiednia pozycja w kolumniei
jest już zajęta przed rozpoczęciem pracy nad tą kolumną. Tj. Jest zajęty przez prawą połowę--
.d[i][j]
to łączna liczba różnych przechyleń.Następnie powiedz
e[i][j]
= suma, nad[i][k]
której możesz umieścić pionowe podstawy domina,k
aby utworzyćj
.e[i][j]
byłaby liczbą przechyleń, w której każdy 1 bitj
jest zajęty przez cokolwiek poza lewą połową--
. Wypełnij je,--
a dostanieszd[i+1][~j]
=e[i][j]
.e[m-1][every bit being 1]
lubd[m][0]
jest ostateczną odpowiedzią.Naiwna implementacja zapewni Ci złożoność czasu gdzieś blisko
g[n]=2*g[n-1]+g[n-2] = O((sqrt(2)+1)^n)
(już wystarczająco szybko, jeśli n = m = 8). Ale zamiast tego możesz najpierw wykonać pętlę dla każdego możliwego domina i spróbować dodać go do każdego kafelka, w którym można dodać to domino, i scalić wynik z oryginalną tablicąd
(jak algorytm problemu Knapsack). I to staje się O (n * 2 ^ n). A wszystko inne to szczegóły implementacji. Cały kod działa w O (m * n * 2 ^ n).źródło
-O1
wydaje się być najsłodszym miejscem. Wypróbowałem wszystkie poziomy optymalizacji.)do
Po rundzie optymalizacji i dostosowanej do zmodyfikowanych reguł:
Zacząłem wpadać na limit długości 1024 znaków, więc musiałem nieco zmniejszyć czytelność. Znacznie krótsze nazwy zmiennych itp.
Instrukcje budowania:
Uruchom z włączonym wyjściem rozwiązania:
Uruchom tylko z liczbą rozwiązań:
Niektóre komentarze:
-O2
wydaje się być dobre.Zauważ też, że kod nadal generuje rzeczywiste rozwiązania, nawet w trybie „tylko zliczanie”. Ilekroć znaleziono rozwiązanie,
vM
maska bitowa zawiera1
dla pozycji, które mają pionowy pasek, oraz0
dla pozycji z poziomym paskiem. Pomijana jest tylko konwersja tej maski bitowej do formatu ASCII i rzeczywiste wyjście.źródło
-O2
powinno być dobre.-O2
wydaje się być najsłodszym miejscem. Wypróbowałem wszystkie poziomy optymalizacji.)do
Chodzi o to, aby najpierw znaleźć wszystkie możliwe układy poziomych domino w rzędzie, przechowywać je,
r[]
a następnie uporządkować, aby uzyskać wszystkie możliwe układy pionowych domino.Kod do pozycjonowania poziomych domino w rzędzie został zmodyfikowany z tej mojej odpowiedzi: https://codegolf.stackexchange.com/a/37888/15599 . Jest wolniejszy w przypadku szerszych siatek, ale nie jest to problemem w przypadku 8x8.
Innowacja polega na sposobie montażu rzędów. Jeśli tablica ma nieparzystą liczbę rzędów, podczas analizy wejściowej jest obracana o 90 stopni, więc ma teraz parzystą liczbę rzędów. Teraz umieszczam pionowe domino wzdłuż linii środkowej. Ze względu na symetrię, jeśli istnieją
c
sposoby ułożenia pozostałych domino w dolnej połowie, muszą istnieć równieżc
sposoby ułożenia pozostałych domino w górnej połowie, co oznacza, że dla danego ułożenia pionowych domino na linii środkowej istniejąc*c
możliwe rozwiązania . Dlatego analizowana jest tylko linia środkowa plus połowa płytki, gdy program musi wydrukować tylko liczbę rozwiązań.f()
buduje tabelę możliwych ustawień domina poziomego i skanuje możliwe ustawienia pionu domina na linii środkowej. następnie wywołuje funkcję rekurencyjną,g()
która wypełnia wiersze. Jeśli wymagane jest drukowanie,h()
wywoływana jest funkcja, aby to zrobić.g()
jest wywoływany z 3 parametrami.y
to bieżący rząd id
kierunek (w górę lub w dół), w którym wypełniamy planszę od środka na zewnątrz.x
zawiera bitmapę, która wskazuje pionowe domino, które są niekompletne z poprzedniego rzędu. Wypróbowywane są wszystkie możliwe układy domino w rzędzie od r []. W tej tablicy 1 oznacza pionowy domino, a para zer poziomy domino. Ważny wpis w tablicy musi mieć co najmniej 1 na tyle, aby zakończyć niekompletne domina pionowy z ostatni wiersz(x&r[j])==x
. Może zawierać więcej 1, co oznacza, że uruchamiane są nowe domino pionowe. Do następnego wiersza potrzebujemy tylko nowych domino, więc ponownie wywołujemy procedurę za pomocąx^r[j]
.Jeśli osiągnięto ostatni rząd i nie ma niekompletnych pionowych domino wiszących na górze lub na dole planszy,
x^r[j]==0
wówczas połowa została pomyślnie ukończona. Jeśli nie drukujemy, wystarczy wypełnić dolną połowę i użyć,c*c
aby obliczyć całkowitą liczbę aranżacji. Jeśli drukujemy, konieczne będzie uzupełnienie górnej połowy, a następnie wywołanie funkcji drukowaniah()
.KOD
Zauważ, że dane wejściowe z nieparzystą liczbą wierszy i parzystą liczbą kolumn są obracane o 90 stopni w fazie analizy. Jeśli jest to niedopuszczalne, funkcję drukowania
h()
można zmienić, aby ją dostosować. (EDYCJA: nie wymagane, patrz komentarze.)EDYCJA: Nowa funkcja
e()
została użyta do sprawdzenia parzystościi
(tj. Liczby domino okalających linię środkową). Parzystośći
(liczba pół-domino na linii środkowej wystających do każdej połowy planszy) musi być taka sama jak dziwność całkowitej liczby spacji w każdej połowie (podana przezn/2
), ponieważ tylko wtedy domina mogą wypełnić całą dostępną przestrzeń. Ta edycja eliminuje połowę wartości i, dzięki czemu mój program jest około dwa razy szybszy.źródło
-O0
było to najlepsze miejsce dla całości. Inne opcje spowolniły kompilację.)32 2 s
. Zatrzymałem to po około 15 minutach.2 32 s
działa prawie natychmiast. Skanowanie wszystkich możliwych pionowych domino jest wyjątkowo marnotrawstwem wH=2
przypadku, ponieważ w rzeczywistości mamy już wszystkie niezbędne informacjer[]
. Jestem bardzo zadowolony z oficjalnego czasu na8 8 s
Oto łatkę do wspomnianego przypadku:if(H==2){C=p;if(!S)for(i=0;i<p;i++)q[0]=q[1]=r[i],h();}else for(i=0;i<=k;i++)c=0,g(H/2,1,i),C+=c*c;
Jak widać, ten fragment będzie działał natychmiast poH=2
ustawieniu flagi. Całkowity czas działania jest następnie ograniczony przez budynek,r[]
który z pewnością ma miejsce na ulepszenia.if(t)for(a=n;a--;a%H||puts(""))putchar(124-(q[a%H]>>a/H)%2*79);else for(a=n;a--;a%W||puts(""))putchar(45+(q[a/W]>>a%W)%2*79);
Długość kodu jest nadal znacznie poniżej 1000 bajtów, a wpływ na czas kompilacji powinien być minimalny. Nie załączyłem tych łat zeszłej nocy, ponieważ byłem zbyt zmęczony.