Hintergrund
Man betrachte eine (geschlossene) Kette von Stäben, von denen jede eine ganzzahlige Länge hat. Wie viele verschiedene lochfreie Polyominoe können Sie mit einer bestimmten Kette bilden? Oder mit anderen Worten, wie viele verschiedene sich nicht selbst schneidende Polygone mit achsenausgerichteten Seiten können Sie mit einer bestimmten Kette bilden?
Schauen wir uns ein Beispiel an. Betrachten Sie eine bestimmte Kette, die aus 8 Stäben der Länge 1 und 2 besteht, die wir als darstellen können [1, 1, 2, 2, 1, 1, 2, 2]
. Bis auf Rotationen und Übersetzungen gibt es nur 8 mögliche Polyominoe (wir zählen verschiedene Reflexionen):
Dieser erste Stab ist dunkelblau, und dann durchlaufen wir das Polygon im Gegenuhrzeigersinn .
Die Drehrichtung beeinflusst das Ergebnis im obigen Beispiel nicht. Betrachten wir jedoch eine andere Kette [3, 1, 1, 1, 2, 1, 1]
, die die folgenden 3 Polyominoe ergibt:
Beachten Sie, dass wir nicht eine Reflexion des letzten polyomino sind, weil sie im Uhrzeigersinn Traversal erfordern würde.
Wenn wir eine flexiblere Kette mit der gleichen Länge [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
hätten, könnten wir tatsächlich beide Reflexionen unter einigen anderen Polyoninoen bilden, insgesamt 9:
Die Herausforderung
Wenn Sie eine Kette, ein Array oder ähnliches beschreiben, bestimmen Sie die Anzahl der verschiedenen Polyominoe, die Sie bilden können (bis zu Rotationen und Verschiebungen), indem Sie die Stäbe der Reihe nach verwenden, während Sie sich im Gegenuhrzeigersinn um den Umfang bewegen .
Bitte schreiben Sie ein vollständiges Programm und fügen Sie Befehle ein, um (falls zutreffend) Ihren Code zu kompilieren und über die Befehlszeile auszuführen. Bitte fügen Sie auch einen Link zu einem kostenlosen Übersetzer / Dolmetscher für Ihre Sprache bei.
Ihr Programm sollte die Eingabe von STDIN lesen. Die erste Zeile wird für eine ganze Zahl enthalten , M . Die nächsten M Zeilen sind Testfälle, von denen jede eine durch Leerzeichen getrennte Liste von Stablängen ist. Ihr Programm sollte M Zeilen an STDOUT ausgeben, von denen jede aus einer einzelnen Ganzzahl besteht - der Anzahl der verschiedenen Polyominoe, die gebildet werden können.
Sie dürfen nur einen Thread verwenden.
Ihr Programm darf zu keinem Zeitpunkt mehr als 1 GB Speicher belegen. (Dies ist keine völlig strenge Beschränkung, aber ich werde die Speichernutzung Ihrer ausführbaren Datei überwachen und jeden Prozess beenden, der durchweg mehr als 1 GB belegt oder deutlich darüber liegt.)
Um übermäßige Vorberechnungen zu vermeiden, darf Ihr Code nicht länger als 20.000 Byte sein und Sie dürfen keine Dateien lesen.
Sie dürfen auch nicht auf die ausgewählten Testfälle hin optimieren (z. B. durch Hardcodierung der Ergebnisse). Wenn ich das vermute, behalte ich mir das Recht vor, neue Benchmark-Sets zu generieren. Die Testsätze sind zufällig, daher sollte die Leistung Ihres Programms für die Leistung bei willkürlichen Eingaben repräsentativ sein. Die einzige Annahme, die Sie machen dürfen, ist, dass die Summe der Stablängen gerade ist.
Wertung
Ich habe Benchmark-Sets für Ketten mit N = 10, 11, ..., 20 Stangen bereitgestellt . Jeder Testsatz enthält 50 zufällige Ketten mit Längen zwischen 1 und einschließlich 4.
Ihre primäre Punktzahl ist die größte N, für die Ihr Programm den gesamten Testsatz innerhalb von 5 Minuten abschließt (auf meinem Computer unter Windows 8). Der Tie Breaker ist die tatsächliche Zeit, die Ihr Programm für diesen Testsatz benötigt.
Wenn jemand das größte Test-Set schlägt, werde ich immer größere hinzufügen.
Testfälle
Mit den folgenden Testfällen können Sie die Richtigkeit Ihrer Implementierung überprüfen.
Input Output
1 1 0
1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1 1 1 9
1 1 1 1 1 1 1 1 1 1 1 1 36
1 1 1 1 1 1 1 1 1 1 1 1 1 1 157
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 758
1 1 2 2 1 1 2 2 8
1 1 2 2 1 1 2 2 1 1 23
1 1 2 2 1 1 2 2 1 1 2 2 69
1 2 1 2 1 2 1 2 3
1 2 1 2 1 2 1 2 1 2 1 2 37
1 2 3 2 1 2 3 2 5
1 2 3 2 1 2 3 2 1 2 3 2 23
3 1 1 1 2 1 1 3
1 2 3 4 5 6 7 1
1 2 3 4 5 6 7 8 3
1 2 3 4 5 6 7 8 9 10 11 5
2 1 5 3 3 2 3 3 4
4 1 6 5 6 3 1 4 2
3 5 3 5 1 4 1 1 3 5
1 4 3 2 2 5 5 4 6 4
4 1 3 2 1 2 3 3 1 4 18
1 1 1 1 1 2 3 3 2 1 24
3 1 4 1 2 2 1 1 2 4 1 2 107
2 4 2 4 2 2 3 4 2 4 2 3 114
Eine Eingabedatei mit diesen finden Sie hier .
Bestenliste
User Language Max N Time taken (MM:SS:mmm)
1. feersum C++ 11 19 3:07:430
2. Sp3000 Python 3 18 2:30:181
quelle
Antworten:
C ++ 11
Aktualisierungen: Es wurde die erste Zeile hinzugefügt, die
c
früh ausbricht, wenn der Abstand zu weit vom Ursprung entfernt ist (das war der ganze Zweck der Variablenrlen
, aber ich habe vergessen, sie in der ersten Version zu schreiben). Ich habe es geändert, um viel weniger Speicher zu verwenden, aber auf Kosten der Zeit. Es löst jetzt N = 20 in knapp 5 Minuten für mich.Kompilieren mit
quelle
#define
s thoPython 3 (mit PyPy ) - N = 18
Laufen Sie mit
./pypy <filename>
.Dies ist die Referenzimplementierung, die ich geschrieben habe, als ich die Frage mit Martin besprochen habe. Es wurde nicht mit dem Gedanken an Geschwindigkeit gemacht und ist ziemlich kitschig, aber es sollte eine gute Basis sein, um die Dinge anzufangen.
N = 18 dauert auf meinem bescheidenen Laptop ungefähr 2,5 Minuten.
Algorithmus
Die Drehungen werden überprüft, indem jede Form in eine Reihe von
F
Vorwärts-,A
Links- undC
Rechtsdrehungen an jedem Gitterpunkt an der Grenze der Form umgewandelt wird - ich nenne dies eine Winkelzeichenfolge . Zwei Formen sind rotatorisch identisch, wenn ihre Winkelketten zyklische Permutationen sind. Anstatt dies immer durch direkten Vergleich zweier Winkelzeichenfolgen zu überprüfen, konvertieren wir eine neue Form vor dem Speichern in eine kanonische Form. Wenn wir einen neuen Kandidaten haben, konvertieren wir in die kanonische Form und prüfen, ob wir diese bereits haben (und nutzen so das Hashing, anstatt den gesamten Satz zu durchlaufen).Die Winkelzeichenfolge wird auch verwendet, um zu überprüfen, ob die Form gegen den Uhrzeigersinn geformt wurde, indem sichergestellt wird, dass die Anzahl
A
s die AnzahlC
s um 4 überschreitet .Die Selbstüberschneidung wird naiv überprüft, indem jeder Gitterpunkt an der Grenze der Form gespeichert und überprüft wird, ob ein Punkt zweimal besucht wird.
Der Kernalgorithmus ist einfach: Platzieren Sie die erste Stange nach rechts und probieren Sie dann alle Möglichkeiten für die verbleibenden Stangen aus. Wenn die Stäbe einen Punkt erreichen, der zu weit vom Ursprung entfernt ist (dh die Summe der verbleibenden Stablängen ist kleiner als der Manhattan-Abstand des Punkts vom Ursprung), hören wir vorzeitig auf, diesen Teilbaum zu suchen.
Updates (neueste zuerst)
quelle