Aufgabe
Schreiben Sie ein Programm, das drei Ganzzahlen m , n entweder aus STDIN oder als Befehlszeilenargumente liest , alle möglichen Kacheln eines Rechtecks der Dimensionen m × n mit 2 × 1 und 1 × 2 Dominos und schließlich die Anzahl der gültigen Kacheln druckt.
Dominos einer einzelnen Kachelung müssen durch zwei Striche ( -
) für 2 × 1 und zwei vertikale Balken ( |
) für 1 × 2 Dominos dargestellt werden. Auf jede Kachelung (einschließlich der letzten) muss ein Zeilenvorschub folgen.
Für Bewertungszwecke müssen Sie auch ein Flag von STDIN oder als Befehlszeilenargument akzeptieren, mit dem Ihr Programm nur die Anzahl der gültigen Kacheln druckt, nicht jedoch die Kacheln selbst.
Ihr Programm darf nicht länger als 1024 Byte sein. Es muss für alle Eingänge so funktionieren, dass m × n ≤ 64 ist .
(Inspiriert von Alle Domino-Kacheln des 4x6-Rechtecks drucken .)
Beispiel
$ sdt 4 2
----
----
||--
||--
|--|
|--|
--||
--||
||||
||||
5
$ sdt 4 2 scoring
5
Wertung
Ihre Punktzahl wird durch die Ausführungszeit Ihres Programms für die Eingabe 8 8 mit gesetztem Flag bestimmt.
Um dies zu einem schnellsten Code und nicht zu einer schnellsten Computerherausforderung zu machen , führe ich alle Einsendungen auf meinem eigenen Computer (Intel Core i7-3770, 16 GiB PC3-12800 RAM) aus, um die offizielle Punktzahl zu ermitteln.
Bitte hinterlassen Sie detaillierte Anweisungen zum Kompilieren und / oder Ausführen Ihres Codes. Wenn Sie eine bestimmte Version des Compilers / Interpreters Ihrer Sprache benötigen, geben Sie eine entsprechende Erklärung ab.
Ich behalte mir das Recht vor, Einsendungen unbewertet zu lassen, wenn:
Für mein Betriebssystem (Fedora 21, 64 Bit) gibt es keinen kostenlosen Compiler / Interpreter (wie bei Bier).
Trotz unserer Bemühungen funktioniert Ihr Code nicht und / oder erzeugt auf meinem Computer eine falsche Ausgabe.
Das Kompilieren oder Ausführen dauert länger als eine Stunde.
Ihr Code oder der einzige verfügbare Compiler / Interpreter enthält einen Systemaufruf
rm -rf ~
oder etwas ähnlich fauliges.
Bestenliste
Ich habe alle Einsendungen neu bewertet, sowohl Kompilierungen als auch Ausführungen in einer Schleife mit 10.000 Iterationen für die Kompilierung und zwischen 100 und 10.000 Iterationen für die Ausführung (abhängig von der Geschwindigkeit des Codes) ausgeführt und den Mittelwert berechnet.
Dies waren die Ergebnisse:
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.
quelle
--
. Wenn es vertikal ist, sind es zwei|
untereinander.Antworten:
C.
Eine unkomplizierte Implementierung ...
Die betrügerische Version
Erklärung des schnelleren Algorithmus
Es scannt von links nach rechts und behält den Zustand bei
d[i][j]
, in dem:i
ist in[0,m)
, was bedeutet, die aktuelle Spalte.j
ist ein Bitvektor der Größen
, wobei das Bit 1 wäre, wenn die entsprechende Position in der Spaltei
bereits belegt ist, bevor mit der Arbeit an dieser Spalte begonnen wird. Dh es ist von der rechten Hälfte eines besetzt--
.d[i][j]
ist die Gesamtzahl der verschiedenen Fliesen.Sagen Sie dann
e[i][j]
= die Summe,d[i][k]
auf die Sie vertikale Dominosteine setzen können, um ak
zu bildenj
.e[i][j]
wäre die Anzahl der Kacheln, bei denen jedes 1 Bit inj
etwas anderem als der linken Hälfte von a belegt ist--
. Füllen Sie sie mit--
und Sie erhaltend[i+1][~j]
=e[i][j]
.e[m-1][every bit being 1]
oderd[m][0]
ist die endgültige Antwort.Eine naive Implementierung bringt Ihnen die zeitliche Komplexität in die Nähe
g[n]=2*g[n-1]+g[n-2] = O((sqrt(2)+1)^n)
(bereits schnell genug, wenn n = m = 8). Sie können jedoch zunächst eine Schleife für jedes mögliche Domino erstellen und versuchen, es zu jeder Kachelung hinzuzufügen, zu der dieses Domino hinzugefügt werden kann, und das Ergebnis mit dem ursprünglichen Array zusammenführend
(wie der Algorithmus für das Knapsack-Problem). Und dies wird O (n * 2 ^ n). Und alles andere sind Implementierungsdetails. Der gesamte Code läuft in O (m * n * 2 ^ n).quelle
-O1
scheint der Sweet Spot zu sein. Ich habe alle Optimierungsstufen ausprobiert.)C.
Nach einer Optimierungsrunde und angepasst an die geänderten Regeln:
Ich habe angefangen, die Längenbeschränkung von 1024 Zeichen zu überschreiten, daher musste ich die Lesbarkeit etwas verringern. Viel kürzere Variablennamen usw.
Bauanleitung:
Mit aktivierter Lösungsausgabe ausführen:
Nur mit Lösungsanzahl ausführen:
Einige Kommentare:
-O2
scheint es gut zu sein.Beachten Sie auch, dass der Code auch im Modus "Nur zählen" die tatsächlichen Lösungen generiert. Immer wenn eine Lösung gefunden wird,
vM
enthält die Bitmaske a1
für Positionen mit vertikalem Balken und a0
für Positionen mit horizontalem Balken. Nur die Konvertierung dieser Bitmaske in das ASCII-Format und die tatsächliche Ausgabe werden übersprungen.quelle
-O2
sollte gut sein.-O2
scheint der Sweet Spot zu sein. Ich habe alle Optimierungsstufen ausprobiert.)C.
Das Konzept besteht darin, zuerst alle möglichen Anordnungen horizontaler Dominosteine in einer Reihe zu finden, sie darin zu speichern
r[]
und dann zu organisieren, um alle möglichen Anordnungen vertikaler Dominosteine zu erhalten.Der Code zum Positionieren der horizontalen Dominosteine in einer Reihe wurde anhand meiner Antwort geändert: /codegolf//a/37888/15599 . Für die breiteren Gitter ist es langsam, aber für den 8x8-Fall ist das kein Problem.
Die Innovation liegt in der Art und Weise, wie die Reihen zusammengesetzt werden. Wenn die Karte eine ungerade Anzahl von Zeilen hat, wird sie beim Parsen der Eingabe um 90 Grad gedreht, sodass sie jetzt eine gerade Anzahl von Zeilen hat. Jetzt platziere ich einige vertikale Dominosteine über der Mittellinie. Wenn es aufgrund der Symmetrie
c
Möglichkeiten gibt, die verbleibenden Dominosteine in der unteren Hälfte anzuordnen, muss es auchc
Möglichkeiten geben, die verbleibenden Dominosteine in der oberen Hälfte anzuordnen, was bedeutet, dass es für eine bestimmte Anordnung vertikaler Dominosteine auf der Mittelliniec*c
mögliche Lösungen gibt . Daher wird nur die Mittellinie plus die Hälfte der Platine analysiert, wenn das Programm nur die Anzahl der Lösungen drucken muss.f()
Erstellt die Tabelle möglicher Anordnungen horizontaler Dominosteine und durchsucht die möglichen Anordnungen vertikaler Dominosteine auf der Mittellinie. es ruft dann eine rekursive Funktion auf,g()
die die Zeilen ausfüllt. Wenn Drucken erforderlich ist, wird dazu die Funktionh()
aufgerufen.g()
wird mit 3 Parametern aufgerufen.y
ist die aktuelle Zeile undd
ist die Richtung (nach oben oder unten), in der wir das Brett von der Mitte nach außen füllen.x
enthält eine Bitmap, die die vertikalen Dominosteine angibt, die aus der vorherigen Zeile unvollständig sind. Alle möglichen Anordnungen von Dominosteinen in einer Reihe von r [] werden versucht. In diesem Array repräsentiert eine 1 ein vertikales Domino und ein Nullpaar ein horizontales Domino. Ein gültiger Eintrag im Array muss mindestens genug Einsen enthalten, um unvollständige vertikale Dominosteine aus der letzten Zeile zu beenden :(x&r[j])==x
. Es kann mehr Einsen geben, was darauf hinweist, dass neue vertikale Dominosteine gestartet werden. Für die nächste Reihe brauchen wir also nur die neuen Dominosteine, also rufen wir die Prozedur erneut mit aufx^r[j]
.Wenn eine Endreihe erreicht wurde und keine unvollständigen vertikalen Dominosteine oben oder unten auf dem Brett hängen, wurde
x^r[j]==0
die Hälfte erfolgreich abgeschlossen. Wenn wir nicht drucken, reicht es aus, die untere Hälfte zu vervollständigen undc*c
die Gesamtzahl der Arrangements zu berechnen. Wenn wir drucken, muss auch die obere Hälfte ausgefüllt und dann die Druckfunktion aufgerufen werdenh()
.CODE
Beachten Sie, dass die Eingabe mit einer ungeraden Anzahl von Zeilen und einer geraden Anzahl von Spalten in der Analysephase um 90 Grad gedreht wird. Wenn dies nicht akzeptabel ist, kann die Druckfunktion
h()
geändert werden, um sie aufzunehmen. (BEARBEITEN: nicht erforderlich, siehe Kommentare.)BEARBEITEN: Eine neue Funktion
e()
wurde verwendet, um die Parität voni
(dh die Anzahl der Dominos auf der Mittellinie) zu überprüfen . Die Parität voni
(die Anzahl der halben Dominosteine auf der Mittellinie, die in jede Hälfte des Bretts hineinragen) muss dieselbe sein wie die Seltsamkeit der Gesamtzahl der Felder in jeder Hälfte (gegeben durchn/2
), weil nur dann die Dominosteine den gesamten verfügbaren Raum ausfüllen können. Diese Bearbeitung eliminiert die Hälfte der Werte von i und macht mein Programm daher ungefähr doppelt so schnell.quelle
-O0
war der Sweet Spot für die Gesamtsumme. Andere Optionen verlangsamten die Kompilierung.)32 2 s
. Ich hörte nach ungefähr 15 Minuten auf.2 32 s
läuft aber fast sofort. Das Durchsuchen aller möglichen vertikalen Dominosteine ist für denH=2
Fall äußerst verschwenderisch , da wir tatsächlich bereits alle erforderlichen Informationen habenr[]
. Ich bin sehr zufrieden mit der offiziellen Zeit für8 8 s
Hier ist ein Patch für den Fall, den Sie erwähnen: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;
Wie Sie sehen können, wird dieses Snippet sofortH=2
mit gesetztem Flag ausgeführt. Die Gesamtlaufzeit wird dann durch das Gebäude begrenzt,r[]
das sicherlich Raum für Verbesserungen bietet.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);
halber hier der Patch, mit dem die Ausgabe bei Bedarf richtig hochgedreht werden kann: Die Codelänge liegt immer noch deutlich unter 1000 Byte, und die Auswirkungen auf die Kompilierungszeit sollten minimal sein. Ich habe diese Patches letzte Nacht nicht aufgenommen, da ich zu müde war.