Tarefa
Escreva um programa que leia três números inteiros m , n de STDIN ou como argumentos da linha de comando, imprima todas as inclinações possíveis de um retângulo de dimensões m × n pelos dominós 2 × 1 e 1 × 2 e, finalmente, o número de inclinações válidas.
Os dominós de uma peça individual devem ser representados por dois traços ( -
) para 2 × 1 e duas barras verticais ( |
) para 1 × 2 dominós. Cada lado a lado (incluindo o último) deve ser seguido por um avanço de linha.
Para fins de pontuação, você também deve aceitar um sinalizador do STDIN ou como argumento da linha de comando que faz com que o programa imprima apenas o número de inclinações válidas, mas não as inclinações em si.
Seu programa não pode ter mais de 1024 bytes. Ele deve funcionar para todas as entradas, de modo que m × n ≤ 64 .
(Inspirado em Imprimir todas as inclinações de dominó do retângulo 4x6 .)
Exemplo
$ sdt 4 2
----
----
||--
||--
|--|
|--|
--||
--||
||||
||||
5
$ sdt 4 2 scoring
5
Pontuação
Sua pontuação é determinada pelo tempo de execução do seu programa para a entrada 8 8 com o sinalizador definido.
Para tornar esse código mais rápido do que um desafio mais rápido para o computador , executarei todos os envios em meu próprio computador (Intel Core i7-3770, 16 GiB PC3-12800 RAM) para determinar a pontuação oficial.
Deixe instruções detalhadas sobre como compilar e / ou executar seu código. Se você precisar de uma versão específica do compilador / intérprete do seu idioma, faça uma declaração nesse sentido.
Reservo-me o direito de deixar os envios sem pontuação se:
Não há compilador / intérprete gratuito (como na cerveja) para o meu sistema operacional (Fedora 21, 64 bits).
Apesar dos nossos esforços, seu código não funciona e / ou produz resultados incorretos no meu computador.
A compilação ou execução leva mais de uma hora.
Seu código ou o único compilador / intérprete disponível contém uma chamada do sistema
rm -rf ~
ou algo igualmente suspeito.
Entre os melhores
Marquei todas as submissões, executando as compilações e as execuções em um loop com 10.000 iterações para compilação e entre 100 e 10.000 iterações para execução (dependendo da velocidade do código) e calculando a média.
Estes foram os resultados:
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.
fonte
--
. Se for vertical, são dois|
, um abaixo do outro.Respostas:
C
Uma implementação direta ...
A versão de trapaça
Explicação do algoritmo mais rápido
Ele digitaliza da esquerda para a direita e mantém o estado
d[i][j]
, onde:i
está em[0,m)
, o que significa a coluna atual.j
é um pequeno vetor de tamanhon
, em que o bit seria 1 se a posição correspondente na colunai
já estiver ocupada antes de começar a trabalhar nessa coluna. Ou seja, é ocupado pela metade direita de um--
.d[i][j]
é o número total de diferentes inclinações.Então diga
e[i][j]
= a soma ded[i][k]
onde você pode colocar a base de dominó verticalk
para formar aj
.e[i][j]
seria o número de inclinações em que cada 1 bitj
é ocupado por qualquer coisa, menos a metade esquerda de a--
. Encha-os com--
e você terád[i+1][~j]
=e[i][j]
.e[m-1][every bit being 1]
oud[m][0]
é a resposta final.Uma implementação ingênua fornecerá a complexidade do tempo em algum lugar próximo
g[n]=2*g[n-1]+g[n-2] = O((sqrt(2)+1)^n)
(já rápido o suficiente se n = m = 8). Mas você pode primeiro fazer um loop para cada dominó possível e tentar adicioná-lo a todos os mosaicos que podem ter esse dominó adicionado e mesclar o resultado à matriz originald
(como o algoritmo para o problema da mochila). E isso se torna O (n * 2 ^ n). E tudo o resto são detalhes de implementação. O código inteiro é executado em O (m * n * 2 ^ n).fonte
-O1
parece ser o ponto ideal. Eu tentei todos os níveis de otimização).C
Após uma rodada de otimizações, e adaptado às regras modificadas:
Comecei a esbarrar no limite de 1024 caracteres, então tive que reduzir um pouco a legibilidade. Nomes de variáveis muito mais curtos etc.
Instruções de construção:
Execute com a saída da solução ativada:
Execute apenas com a contagem de soluções:
Alguns comentários:
-O2
parece bom.Observe também que o código ainda gera as soluções reais, mesmo no modo "contar apenas". Sempre que uma solução é encontrada, a
vM
máscara de bits contém um1
para as posições que possuem uma barra vertical e um0
para posições com uma barra horizontal. Somente a conversão dessa máscara de bits no formato ASCII e a saída real são ignoradas.fonte
-O2
deve ser bom.-O2
parece ser o ponto ideal. Tentei todos os níveis de otimização).C
O conceito é encontrar primeiro todos os arranjos possíveis de dominós horizontais em uma fileira, armazená-los em
r[]
e organizá-los para fornecer todos os arranjos possíveis de dominós verticais.O código para posicionar os dominós horizontais em uma linha é modificado a partir desta resposta minha: https://codegolf.stackexchange.com/a/37888/15599 . É lento para as grades mais largas, mas isso não é um problema para o gabinete 8x8.
A inovação está na maneira como as linhas são montadas. Se o quadro tiver um número ímpar de linhas, ele será girado 90 graus na análise de entrada, portanto, agora ele terá um número par de linhas. Agora, coloco alguns dominós verticais na linha central. Por causa da simetria, se existem
c
maneiras de organizar os dominós restantes na metade inferior, também deve haverc
maneiras de organizar os dominós restantes na metade superior, o que significa que, para um determinado arranjo de dominós verticais na linha central, existemc*c
soluções possíveis . Portanto, apenas a linha central e mais metade da placa é analisada quando o programa é obrigado a imprimir apenas o número de soluções.f()
constrói a tabela de possíveis arranjos de dominó horizontal e varre os possíveis arranjos de dominó vertical na linha central. em seguida, chama a função recursivag()
que preenche as linhas. Se for necessária impressão, a funçãoh()
é chamada para fazer isso.g()
é chamado com 3 parâmetros.y
é a linha atual ed
é a direção (para cima ou para baixo) na qual estamos preenchendo o quadro do centro para o exterior.x
contém um bitmap indica os dominós verticais incompletos da linha anterior. Todos os arranjos possíveis de dominós seguidos de r [] são tentados. Nesta matriz, um 1 representa um dominó vertical e um par de zeros um dominó horizontal. A entrada válida na matriz deve ter pelo menos o suficiente 1s para terminar quaisquer dominó verticais incompletas a partir da última linha:(x&r[j])==x
. Pode ter mais 1s, o que indica que novos dominós verticais estão sendo iniciados. Para a próxima linha, então, precisamos apenas dos novos dominós, então chamamos o procedimento novamente comx^r[j]
.Se uma linha final foi alcançada e não há dominós verticais incompletos pendurados na parte superior ou inferior do tabuleiro
x^r[j]==0
, a metade foi concluída com êxito. Se não estivermos imprimindo, é suficiente concluir a metade inferior e usá-lac*c
para calcular o número total de arranjos. Se estivermos imprimindo, será necessário concluir também a metade superior e chamar a função de impressãoh()
.CÓDIGO
Observe que a entrada com um número ímpar de linhas e número par de colunas é girada em 90 graus na fase de análise. Se isso não for aceitável, a função de impressão
h()
pode ser alterada para acomodá-la. (EDIT: não é necessário, veja os comentários.)EDIT: Uma nova função
e()
foi utilizado para verificar a paridade dei
(ou seja, o número de dominó abrangendo a linha central). A paridade dei
(o número de meias-dominó no eixo saliente em cada metade da placa) deve ser a mesma que a singularidade do número total de espaços em cada metade (dado porn/2
), porque somente então os dominós podem preencher todo o espaço disponível. Essa edição elimina metade dos valores de i e, portanto, torna meu programa aproximadamente duas vezes mais rápido.fonte
-O0
foi o ponto doce para o total Outras opções abrandou compilação..)32 2 s
. Eu parei depois de 15 minutos.2 32 s
é executado quase instantaneamente. A varredura em todos os possíveis dominós verticais é extremamente inútil para oH=2
caso, porque na verdade já temos todas as informações necessáriasr[]
. Estou muito satisfeito com o horário oficial de8 8 s
Aqui está um patch para o caso que você mencionou: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;
Como você pode ver, esse trecho será executado instantaneamenteH=2
com o sinalizador definido. O tempo de execução geral é então limitado pelo edifício,r[]
que certamente tem espaço para melhorias.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);
comprimento do código ainda está bem abaixo de 1000 bytes e o impacto no tempo de compilação deve ser mínimo. Não incluí esses adesivos ontem à noite, pois estava cansado demais.