Dada uma imagem em preto e branco com fundo branco e um conjunto de pontos pretos, pinte um conjunto de pixels brancos em vermelho, para que haja um caminho entre cada par de pixels pretos.
Detalhes
Um caminho é um conjunto de pixels conectados (conectividade de 8 vizinhanças). Pixels pretos podem ser usados como parte dos caminhos. O objetivo é tentar minimizar o conjunto de pixels vermelhos nas condições acima e gerar uma imagem correspondente.
Você não precisa encontrar a solução ideal.
Uma solução trivial e ao mesmo tempo pior é pintar todos os pixels brancos em vermelho.
Exemplo (os pixels são ampliados para visibilidade):
Detalhes
- Dada uma imagem de pixel (em qualquer formato adequado), retorne outra imagem com os pontos conectados conforme especificado acima, bem como um número inteiro indicando quantos pixels vermelhos foram usados.
- A Pontuação é o produto de (1 + o número de pixels vermelhos) para cada um dos 14 casos de teste.
- O objetivo é ter a menor pontuação.
Casos de teste
As 14 caixas de teste são mostradas abaixo. Um programa python para verificar a conexão das saídas pode ser encontrado aqui.
Meta
Obrigado a @Veskah, @Fatalize, @ wizzwizz4 e @trichoplax pelas várias sugestões.
Respostas:
C, pontuação 2.397x10 ^ 38
Cara, isso demorou muito para ser feito, provavelmente devido à minha escolha de idioma. Coloquei o algoritmo trabalhando bastante cedo, mas tive muitos problemas com a alocação de memória (não era possível liberar material recursivamente devido a estouros de pilha, o tamanho dos vazamentos era enorme).
Ainda! Ele supera a outra entrada em todos os casos de teste e
pode até ser o ideal,aproxima-se bastante ou é a solução ideal muitas vezes.Enfim, aqui está o código:
Testado em: Arch Linux, GCC 9.1.0,
-O3
Esse código recebe entrada / saída em um arquivo personalizado que chamo de "cppm" (porque é como uma versão condensada do formato PPM clássico). Um script python para converter de / para ele está abaixo:
Explicação do algoritmo
O funcionamento desse algoritmo é que ele começa encontrando todas as formas conectadas na imagem, incluindo pixels vermelhos. Ele pega o primeiro e expande sua fronteira, um pixel de cada vez, até encontrar outra forma. Em seguida, pinta todos os pixels do toque à forma original (usando a lista vinculada criada ao longo do caminho para acompanhar). Por fim, repete o processo, encontrando todas as novas formas criadas, até que haja apenas uma forma.
Galeria de imagens
Testcase 1, 183 pixelsTestcase 2, 140 pixels
Testcase 3, 244 pixels
Testcase 4, 42 pixels
Testcase 5, 622 pixels
Testcase 6, 1 pixel
Testcase 7, 104 pixels
Testcase 8, 2286 pixels
Testcase 9, 22 pixels
Testcase 10, 31581 pixels
Testcase 11, 21421 pixels
Testcase 12, 5465 pixels
Testcase 13, 4679 pixels
Testcase 14, 7362 pixels
fonte
Python, 2,62 * 10 ^ 40
Esse algoritmo apenas preenche (BFS) o plano a partir das partes pretas da imagem, onde para cada novo pixel registramos a parte preta da qual foi inundada. Assim que temos dois pixels vizinhos com diferentes partes pretas como ancestrais, basicamente mesclamos essas duas partes pretas juntando-as através dos ancestrais dos dois vizinhos que acabamos de encontrar. Em teoria, isso poderia ser implementado
O(#pixels)
, mas para manter a quantidade de código em um nível aceitável, essa implementação é um pouco pior.Resultado
Ponto
fonte
Python 3:
1.7x10 ^ 421.5x10 ^ 41Usando
Pillow
,numpy
escipy
.Supõe-se que as imagens estejam em uma
images
pasta localizada no mesmo diretório que o script.Isenção de responsabilidade : leva muito tempo para processar todas as imagens.
Código
Explicação
Solução trivial. Começamos alterando a cor de todos os pixels brancos em uma imagem para vermelho. Ao fazer isso, é garantido que todos os elementos (qualquer ilha de pixels pretos) estejam conectados.
Em seguida, iteramos sobre todos os pixels da imagem, começando no canto superior esquerdo e movendo para a direita e para baixo. Para cada pixel vermelho, descobrimos que mudamos sua cor para branco. Se após essa mudança de cor ainda houver apenas um elemento (um elemento agora sendo qualquer ilha de pixels pretos e vermelhos), deixamos o pixel branco e passamos para o próximo pixel. No entanto, se depois que a cor mudar de vermelho para branco, o número de elementos for maior que um, deixamos o pixel vermelho e passamos para o próximo pixel.
Atualizar
Como pode ser visto (e esperado), as conexões obtidas usando apenas este método mostram um padrão regular e, em alguns casos, como nas imagens 6 e 11, existem pixels vermelhos desnecessários.
Esses pixels vermelhos extras podem ser facilmente removidos iterando novamente sobre a imagem e executando as mesmas operações explicadas acima, mas do canto inferior direito ao canto superior esquerdo. Esta segunda passagem é muito mais rápida, pois é necessário verificar a quantidade de pixels vermelhos.
Resultados
As imagens modificadas após a segunda passagem são listadas duas vezes para mostrar as diferenças.
Número de pixels vermelhos: 18825
Número de pixels vermelhos: 334
Número de pixels vermelhos: 1352
Número de pixels vermelhos: 20214
Número de pixels vermelhos: 47268
Número de pixels vermelhos:
63.27Número de pixels vermelhos: 17889
Número de pixels vermelhos: 259
Número de pixels vermelhos: 6746
Número de pixels vermelhos: 586
Número de pixels vermelhos:
91Número de pixels vermelhos: 126
Número de pixels vermelhos: 212
Número de pixels vermelhos: 683
Cálculo da pontuação:
(1 + 6746) * (1 + 126) * (1 + 259) * (1 + 17889) * (1 + 334) * (1 + 586) * (1 + 18825) * (1 + 9) * (1 +683) * (1 + 1352) * (1 + 20214) * (1 + 212) * (1 + 63) * (1 + 47268) = 1778700054505858720992088713763655500800000 ~ 1,7x10 ^ 42
Computação de pontuação atualizada após adicionar a segunda passagem:
(1+ 18825) * (1+ 1352) * (1+ 20214) * (1+ 47268) * (1+ 27) * (1+ 17889) * (1+ 6746) * (1+ 586) * (1+ + 1) * (1+ 126) * (1+ 212) * (1+ 334) * (1 + 259) * (1 + 683) = 155636254769262638086807762454319856320000 ~ 1,5x10 ^ 41
fonte