Um nonograma é um jogo de quebra-cabeça japonês no qual o objetivo é desenhar uma imagem em preto e branco de acordo com uma lista de regiões contíguas, como:
Defina a magnitude não geográfica de uma linha ou coluna para ser o número de regiões negras contíguas nessa linha ou coluna. Por exemplo, a linha superior tem uma magnitude não geográfica de 1, porque há uma região de 2 quadrados nessa linha. A 8a linha tem uma magnitude não-gráfica de 3 porque possui 2, 2, 1.
Uma linha ou coluna vazia possui uma magnitude não geográfica de 0.
Sua tarefa é escrever um programa que utiliza uma grade de solução para um nonograma e produz uma grade de solução com o menor número possível de quadrados preenchidos, onde cada linha e coluna tem o mesmo magnutídeo não-gráfico da grade de solução fornecida.
Por exemplo, uma grade de não-programa com todos os quadrados preenchidos possui uma magnitude não-geográfica de 1 em cada linha ou coluna:
A mesma magnitude não-geográfica poderia ser alcançada apenas com uma faixa diagonal na grade, reduzindo drasticamente o número de quadrados preenchidos:
Seu programa receberá uma entrada composta por 50.000 linhas desse arquivo ( arquivo de texto tar.gz de 1,32 MB; descompactado 2,15 MB), cada uma representando uma única grade de solução de nonograma 16 × 16 com quadrados preenchidos aleatoriamente (80% em preto) e produzir outras 50.000 linhas, cada uma contendo a grade de solução otimizada para a grade de entrada correspondente.
Cada grade é representada como uma string base64 com 43 caracteres (codificando quadrados da esquerda para a direita e depois de cima para baixo), e seu programa precisará retornar sua saída no mesmo formato. Por exemplo, a primeira grade do arquivo é E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=
e é renderizada como:
A grade começa com a E
qual é mapeada 000100
, portanto, as seis primeiras células da linha superior são todas brancas, exceto a quarta. O próximo caractere é o /
qual é mapeado 111111
, então as próximas 6 células são todas pretas - e assim por diante.
Seu programa deve realmente retornar uma grade de solução com as magnitudes não-geográficas corretas para cada um dos 50.000 casos de teste. É permitido retornar a mesma grade que a entrada se nada melhor foi encontrado.
O programa para retornar o menor número total de quadrados preenchidos (em qualquer idioma) é o vencedor, com o código mais curto sendo o desempate.
Painel atual:
- 3.637.260 - Sleafar, Java
- 7.270.894 - flawr, Matlab
- 10.239.288 - Joe Z., Bash
fonte
Respostas:
Python 2 e PuLP - 2.644.688 quadrados (minimizado idealmente); 10.753.553 quadrados (maximizado da melhor maneira)
Golfe mínimo para 1152 bytes
(NB: as linhas fortemente recuadas começam com tabulações, não espaços.)
Exemplo de saída: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing
Acontece que problemas como esses são facilmente conversíveis em Programas Lineares Inteiros, e eu precisava de um problema básico para aprender a usar o PuLP - uma interface python para uma variedade de solucionadores de LP - para um projeto meu. Acontece também que o PuLP é extremamente fácil de usar, e o construtor de LP despojado funcionou perfeitamente na primeira vez que o experimentei.
As duas coisas boas sobre o emprego de um solucionador de IP ramificado e vinculado para fazer o trabalho duro de resolver isso para mim (além de não ter que implementar um solucionador de ramificação e vinculado) são:
Como usar este programa
Primeiro, você precisará instalar o PuLP.
pip install pulp
deve fazer o truque se você tiver o pip instalado.Em seguida, será necessário colocar o seguinte em um arquivo chamado "c": https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing
Em seguida, execute este programa em qualquer versão posterior do Python 2 do mesmo diretório. Em menos de um dia, você terá um arquivo chamado "s" que contém 50.000 grades não programadas (em formato legível), cada uma com o número total de quadrados preenchidos listados abaixo.
Se você deseja maximizar o número de quadrados preenchidos, altere a
LpMinimize
linha 8 paraLpMaximize
. Você obterá resultados muito parecidos com este: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharingFormato de entrada
Este programa usa um formato de entrada modificado, já que Joe Z. disse que poderíamos recodificar o formato de entrada se gostarmos de um comentário sobre o OP. Clique no link acima para ver como é. Consiste em 10000 linhas, cada uma contendo 16 números. As linhas numeradas pares são as magnitudes das linhas de uma determinada instância, enquanto as linhas numeradas ímpares são as magnitudes das colunas da mesma instância que a linha acima delas. Este arquivo foi gerado pelo seguinte programa:
(Esse programa de recodificação também me deu uma oportunidade extra de testar minha classe BitQueue personalizada que criei para o mesmo projeto mencionado acima. É simplesmente uma fila na qual os dados podem ser enviados como sequências de bits OU bytes e a partir dos quais os dados podem ser exibido um pouco ou um byte de cada vez. Nesse caso, funcionou perfeitamente.)
Recodifiquei a entrada pelo motivo específico de que, para construir um ILP, as informações extras sobre as grades usadas para gerar as magnitudes são perfeitamente inúteis. As magnitudes são as únicas restrições e, portanto, as magnitudes são tudo o que eu precisava acessar.
Construtor ILP Ungolfed
Este é o programa que realmente produziu a "saída de exemplo" vinculada acima. Daí as cordas extra longas no final de cada grade, que eu truncava ao jogar no golfe. (A versão golfada deve produzir saída idêntica, menos as palavras
"Filled squares for "
)Como funciona
Eu uso uma grade 18x18, com a parte central 16x16 sendo a solução real do quebra-cabeça.
cells
é essa grade. A primeira linha cria 324 variáveis binárias: "cell_0_0", "cell_0_1" e assim por diante. Também crio grades dos "espaços" entre e ao redor das células na parte da solução da grade.rowseps
aponta para as 289 variáveis que simbolizam os espaços que separam as células horizontalmente, enquanto dacolseps
mesma forma aponta para as variáveis que marcam os espaços que separam as células verticalmente. Aqui está um diagrama unicode:Os
0
s e os□
são os valores binários rastreados pelascell
variáveis, os|
s são os valores binários rastreados pelasrowsep
variáveis e os-
s são os valores binários rastreados pelascolsep
variáveis.Esta é a função objetivo. Apenas a soma de todas as
cell
variáveis. Como essas são variáveis binárias, esse é exatamente o número de quadrados preenchidos na solução.Isso apenas define as células ao redor da borda externa da grade como zero (e é por isso que eu as representei como zeros acima). Essa é a maneira mais conveniente de rastrear quantos "blocos" de células são preenchidos, pois garante que cada alteração de não preenchida para preenchida (movendo-se através de uma coluna ou linha) seja correspondida por uma alteração correspondente de preenchida para não preenchida (e vice-versa ), mesmo que a primeira ou a última célula da linha esteja preenchida. Esse é o único motivo para usar uma grade 18x18 em primeiro lugar. Não é a única maneira de contar blocos, mas acho que é a mais simples.
Esta é a verdadeira carne da lógica do ILP. Basicamente, exige que cada célula (exceto as da primeira linha e coluna) seja o xor lógico da célula e o separador diretamente à sua esquerda na linha e diretamente acima dela na coluna. Eu recebi as restrições que simulam um xor em um programa inteiro {0,1} com esta resposta maravilhosa: /cs//a/12118/44289
Para explicar um pouco mais: essa restrição xor permite que os separadores possam ser 1 se e somente se estiverem entre células que são 0 e 1 (marcando uma mudança de não preenchida para preenchida ou vice-versa). Assim, haverá exatamente o dobro de separadores com valor 1 em uma linha ou coluna do número de blocos nessa linha ou coluna. Em outras palavras, a soma dos separadores em uma determinada linha ou coluna é exatamente o dobro da magnitude dessa linha / coluna. Daí as seguintes restrições:
E é isso mesmo. O restante apenas pede ao solucionador padrão que resolva o ILP e formata a solução resultante enquanto a grava no arquivo.
fonte
Java,
6.093.0924.332.6563.637.260 quadrados (minimizado),10.567.55010.567.69110.568.746 quadrados (maximizado)Ambas as variantes do programa executam operações repetidamente na grade de origem, sem alterar a magnitude.
Minimizador
encolher()
Se um quadrado preto tiver 2 vizinhos brancos e 2 vizinhos pretos em um ângulo de 90 °, ele poderá ser substituído por um quadrado branco.
moveLine ()
Em configurações como acima, a linha preta pode ser movida para a direita. Isso é feito repetidamente para todas as quatro direções da linha no sentido horário e anti-horário, para abrir novas possibilidades de redução.
Maximizer
Remova o comentário da linha
main()
e comente a linha acima desta versão.crescer()
Se um quadrado branco tiver 2 vizinhos brancos e 2 vizinhos pretos em um ângulo de 90 °, ele poderá ser substituído por um quadrado preto.
moveLine ()
O mesmo que no Minimizer.
Fonte
fonte
Bash - 10.239.288 quadrados
Como uma solução de referência de último lugar:
Como seu programa tem permissão para retornar a mesma grade se não encontrar uma solução melhor, imprimir todo o arquivo literalmente também é válido.
Há um total de 10.239.288 quadrados pretos no arquivo de teste, que é bem próximo dos 10.240.000 que você esperaria de 80% dos quadrados preenchidos em 50.000 grades com 256 quadrados cada. Como sempre, com minhas perguntas sobre bateria de teste, selecionei o número de casos de teste com a expectativa de que as pontuações ideais estejam em torno de 2 milhões, embora eu suspeite que as pontuações estejam mais próximas de 4 ou 5 milhões desta vez. .
Se alguém puder criar uma solução que maximize os quadrados pretos em vez de minimizá-los e consiga superar 10.240.000, eu poderia considerar dar uma recompensa.
fonte
Matlab, 7.270.894 quadrados (~ 71% do original)
A idéia é uma pesquisa repetida simples e gananciosa: para cada quadrado preto, tente se você pode defini-lo para branco sem alterar as magnitudes não-geográficas. Repita isso duas vezes. (Você pode obter resultados muito melhores com mais repetições, mas não de graça: resulta em um tempo de execução correspondentemente mais longo. Agora, eram cerca de 80 minutos. Eu faria isso, se não precisássemos calcular todos os casos de teste de 50 mil ...)
Aqui o código (cada função em um arquivo separado, como de costume.)
fonte