Tâche
Écrivez un programme qui lit trois entiers m , n à partir de STDIN ou comme arguments de ligne de commande, imprime tous les pavages possibles d'un rectangle de dimensions m × n par des dominos 2 × 1 et 1 × 2 et enfin le nombre de pavages valides.
Les dominos d'un pavage individuel doivent être représentés par deux tirets ( -
) pour les dominos 2 × 1 et deux barres verticales ( |
) pour les dominos 1 × 2 . Chaque pavage (y compris le dernier) doit être suivi d'un saut de ligne.
À des fins d'évaluation, vous devez également accepter un indicateur de STDIN ou comme argument de ligne de commande qui fait que votre programme imprime uniquement le nombre de pavages valides, mais pas les pavages lui-même.
Votre programme ne doit pas dépasser 1 024 octets. Il doit fonctionner pour toutes les entrées telles que m × n ≤ 64 .
(Inspiré par Imprimer tous les pavages domino de rectangle 4x6 .)
Exemple
$ sdt 4 2
----
----
||--
||--
|--|
|--|
--||
--||
||||
||||
5
$ sdt 4 2 scoring
5
Notation
Votre score est déterminé par le temps d'exécution de votre programme pour l'entrée 8 8 avec le drapeau défini.
Pour en faire un code plus rapide qu'un défi informatique plus rapide , je vais exécuter toutes les soumissions sur mon propre ordinateur (Intel Core i7-3770, 16 Go de RAM PC3-12800) pour déterminer le score officiel.
Veuillez laisser des instructions détaillées sur la façon de compiler et / ou d'exécuter votre code. Si vous avez besoin d'une version spécifique du compilateur / interprète de votre langue, faites une déclaration à cet effet.
Je me réserve le droit de laisser les soumissions non notées si:
Il n'y a pas de compilateur / interprète gratuit (comme dans la bière) pour mon système d'exploitation (Fedora 21, 64 bits).
Malgré nos efforts, votre code ne fonctionne pas et / ou produit une sortie incorrecte sur mon ordinateur.
La compilation ou l'exécution prennent plus d'une heure.
Votre code ou le seul compilateur / interprète disponible contient un appel système
rm -rf ~
ou quelque chose de tout aussi louche.
Classement
J'ai réévalué toutes les soumissions, en exécutant les compilations et les exécutions en boucle avec 10000 itérations pour la compilation et entre 100 et 10000 itérations pour l'exécution (en fonction de la vitesse du code) et en calculant la moyenne.
Voici les résultats:
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.
la source
--
. Si c'est vertical, c'est deux|
, l'un en dessous de l'autre.Réponses:
C
Une implémentation simple ...
La version triche
Explication de l'algorithme plus rapide
Il scanne de gauche à droite et maintient l'état
d[i][j]
, où:i
est dans[0,m)
, ce qui signifie la colonne actuelle.j
est un vecteur bit de taillen
, où le bit serait 1 si la position correspondante dans la colonnei
est déjà occupée avant de commencer à travailler sur cette colonne. C'est à dire qu'il est occupé par la moitié droite d'un--
.d[i][j]
est le nombre total de pavages différents.Dites ensuite
e[i][j]
= la somme de l'd[i][k]
endroit où vous pouvez placer la base des dominos verticauxk
pour former unj
.e[i][j]
serait le nombre de pavages où chaque bit 1j
est occupé par autre chose que la moitié gauche d'un--
. Remplissez-les avec--
et vous obtiendrezd[i+1][~j]
=e[i][j]
.e[m-1][every bit being 1]
oud[m][0]
est la réponse finale.Une implémentation naïve vous donnera la complexité temporelle quelque part près
g[n]=2*g[n-1]+g[n-2] = O((sqrt(2)+1)^n)
(déjà assez rapide si n = m = 8). Mais vous pouvez au lieu de cela d'abord boucler pour chaque domino possible, et essayer de l'ajouter à chaque mosaïque qui peut avoir ce domino ajouté, et fusionner le résultat dans le tableau d'origined
(comme l'algorithme pour le problème du sac à dos). Et cela devient O (n * 2 ^ n). Et tout le reste sont des détails d'implémentation. L'ensemble du code s'exécute en O (m * n * 2 ^ n).la source
-O1
semble être le point idéal. J'ai essayé tous les niveaux d'optimisation.)C
Après une série d'optimisations, et adapté aux règles modifiées:
J'ai commencé à me heurter à la longueur maximale de 1024 caractères, j'ai donc dû réduire quelque peu la lisibilité. Noms de variables beaucoup plus courts, etc.
Instructions de construction:
Exécuter avec la sortie de solution activée:
Exécuter avec uniquement le nombre de solutions:
Certains commentaires:
-O2
semble être bon.Notez également que le code génère toujours les solutions réelles, même en mode "comptage uniquement". Chaque fois qu'une solution est trouvée, le
vM
masque de bits contient un1
pour les positions qui ont une barre verticale et un0
pour les positions avec une barre horizontale. Seule la conversion de ce masque binaire au format ASCII et la sortie réelle sont ignorées.la source
-O2
devrait être bon.-O2
semble être le point idéal. J'ai essayé tous les niveaux d'optimisation.)C
Le concept est de trouver d'abord tous les arrangements possibles de dominos horizontaux dans une rangée, de les stocker
r[]
puis de les organiser pour donner tous les arrangements possibles de dominos verticaux.Le code de positionnement des dominos horizontaux dans une rangée est modifié à partir de ma réponse: https://codegolf.stackexchange.com/a/37888/15599 . C'est lent pour les grilles plus larges mais ce n'est pas un problème pour le boîtier 8x8.
L'innovation réside dans la façon dont les rangées sont assemblées. Si la carte a un nombre impair de lignes, elle est tournée de 90 degrés dans l'analyse en entrée, elle a donc maintenant un nombre pair de lignes. Maintenant, je place des dominos verticaux à travers la ligne médiane. En raison de la symétrie, s'il existe des
c
moyens de disposer les dominos restants dans la moitié inférieure, il doit également y avoir desc
moyens de disposer les dominos restants dans la moitié supérieure, ce qui signifie que pour une disposition donnée de dominos verticaux sur la ligne médiane, il existec*c
des solutions possibles . Par conséquent, seule la ligne médiane plus la moitié de la carte est analysée lorsque le programme doit imprimer uniquement le nombre de solutions.f()
construit le tableau des dispositions possibles des dominos horizontaux et analyse les dispositions possibles des dominos verticaux sur la ligne médiane. il appelle ensuite la fonction récursiveg()
qui remplit les lignes. Si l'impression est requise, une fonctionh()
est appelée pour ce faire.g()
est appelé avec 3 paramètres.y
est la ligne actuelle etd
est la direction (vers le haut ou vers le bas) dans laquelle nous remplissons la planche du centre vers l'extérieur.x
contient un bitmap indique les dominos verticaux qui sont incomplets de la ligne précédente. Toutes les dispositions possibles des dominos consécutifs de r [] sont essayées. Dans ce tableau, un 1 représente un domino vertical et une paire de zéros un domino horizontal. Une entrée valable dans la matrice doit avoir au moins suffisamment de 1 pour terminer tous les dominos verticaux incomplets de la dernière ligne:(x&r[j])==x
. Il peut y avoir plus de 1, ce qui indique que de nouveaux dominos verticaux sont en cours de démarrage. Pour la ligne suivante, nous n'avons besoin que des nouveaux dominos, nous appelons donc à nouveau la procédure avecx^r[j]
.Si une rangée de fin a été atteinte et qu'il n'y a pas de dominos verticaux incomplets suspendus en haut ou en bas de la planche,
x^r[j]==0
la moitié a été complétée avec succès. Si nous n'imprimons pas, il suffit de compléter la moitié inférieure et d'utiliserc*c
pour calculer le nombre total d'arrangements. Si nous imprimons, il sera nécessaire de compléter également la moitié supérieure, puis d'appeler la fonction d'impressionh()
.CODE
Notez que l'entrée avec un nombre impair de lignes et un nombre pair de colonnes est tournée de 90 degrés dans la phase d'analyse. Si cela est inacceptable, la fonction d'impression
h()
peut être modifiée pour l'adapter. (EDIT: non requis, voir les commentaires.)EDIT: Une nouvelle fonction
e()
a été utilisée pour vérifier la parité dei
(c'est-à-dire le nombre de dominos chevauchant la ligne médiane). La parité dei
(le nombre de demi-dominos sur la ligne médiane faisant saillie dans chaque moitié de la planche) doit être la même que la bizarrerie du nombre total d'espaces dans chaque moitié (donnée parn/2
) car alors seulement les dominos peuvent remplir tout l'espace disponible. Cette modification élimine la moitié des valeurs de i et rend donc mon programme environ deux fois plus rapide.la source
-O0
était le point idéal pour le total. D'autres options ont ralenti la compilation.)32 2 s
. Je l'ai arrêté après environ 15 minutes.2 32 s
fonctionne pourtant presque instantanément. La numérisation à travers tous les dominos verticaux possibles est extrêmement inutile pour leH=2
boîtier, car en fait, nous avons déjà toutes les informations nécessairesr[]
. Je suis très satisfait de l'heure officielle de8 8 s
Voici un patch pour le cas que vous mentionnez: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;
Comme vous pouvez le voir, cet extrait sera exécuté instantanémentH=2
avec le jeu de drapeaux. Le temps d'exécution global est alors limité par le bâtimentr[]
qui a certainement des améliorations à apporter.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);
longueur de code est toujours bien inférieure à 1 000 octets et l'impact sur le temps de compilation devrait être minime. Je n'ai pas inclus ces patchs hier soir car j'étais trop fatigué.