Introdução
Você é um biólogo que estuda os padrões de movimento das bactérias. Sua equipe de pesquisa tem vários deles em uma placa de Petri e você está registrando a atividade deles. Infelizmente, você está seriamente subfinanciado e não pode pagar por uma câmera de vídeo; portanto, basta tirar uma foto do prato em intervalos regulares. Sua tarefa é criar um programa que rastreie os movimentos dos germes a partir dessas figuras.
Entrada
Suas entradas são duas matrizes 2D de caracteres em qualquer formato razoável, representando imagens consecutivas da placa de Petri. Nas duas matrizes, o caractere .
representa espaço vazio e O
representa um germe (você pode escolher dois caracteres distintos, se desejar). Além disso, a matriz "depois" é obtida da matriz "antes" movendo alguns germes um passo em uma das quatro direções cardinais; em particular, as matrizes têm a mesma forma. Os germes se movem simultaneamente, portanto, um deles pode se mover para um espaço que já continha outro germe, se sair do caminho. É garantido que as bordas da matriz "before" contenham apenas espaços vazios e que haja pelo menos um germe. Assim, o seguinte é um par válido de entradas:
Before After
...... ......
.O..O. ....O.
.OO.O. .OO.O.
...... ..O...
Saída
Sua saída é uma única matriz 2D de caracteres no mesmo formato que as entradas. É obtido da matriz "before" substituindo os germes que foram movidos por um dos >^<v
, dependendo da direção do movimento (você também pode usar 4 caracteres distintos aqui). Pode haver várias saídas possíveis, mas você deve fornecer apenas uma delas. No exemplo acima, uma saída correta possível é
......
.v..O.
.>v.O.
......
Movimento desnecessário é permitido na saída e germes podem trocar de lugar, portanto o seguinte também é válido:
......
.v..v.
.>v.^.
......
Regras e pontuação
Você pode escrever um programa completo ou uma função. A menor contagem de bytes vence e as brechas padrão não são permitidas.
Estou interessado em algoritmos relativamente eficientes, mas não quero banir totalmente a força bruta. Por esse motivo, há um bônus de -75% para resolver o último caso de teste em 10 minutos em uma CPU moderna (não consigo testar a maioria das soluções, por isso confio em você aqui). Isenção de responsabilidade: eu sei que existe um algoritmo rápido (procure por "problema de caminhos disjuntos"), mas não o implementei.
Casos de teste adicionais
Before
......
.O..O.
..OO..
......
After
......
..O...
...OO.
..O...
Possible output
......
.>..v.
..vO..
......
Before
.......
.OOOOO.
.O..OO.
.OO..O.
.OOOOO.
.......
After
.......
..OOOOO
.O...O.
.O...O.
.OOOOOO
....O..
Possible output
.......
.>>>>>.
.O..>v.
.Ov..v.
.O>>v>.
.......
Before
..........
.OOO..OOO.
.OOOOOOOO.
.OOO..OOO.
..........
After
..O.......
.OOO..O.O.
..OOOOOOOO
.O.O..OOO.
.......O..
Possible output
..........
.>^O..O>v.
.^O>>>vO>.
.O>^..>vO.
..........
Before
............
.OO..OOOOOO.
.OO......OO.
...OOOOOO...
.O.OOOOOO.O.
...OOOOOO...
.OOOOOOOOOO.
............
After
..........O.
.OO..OOOOO..
.O...O...O..
.O.OOOOOOO..
.O.OOOOOO..O
...OO..OO...
....OOOOOOOO
.OOO........
Possible output
............
.OO..v<<<<^.
.v<......^<.
...OOO>>>...
.O.OOO^OO.>.
...OOv^OO...
.vvvO>>>>>>.
............
Before
................
.OOOOOO.OOOOOOO.
..OO..OOOOOOOOO.
.OOO..OOOO..OOO.
..OOOOOOOO..OOO.
.OOOOOOOOOOOOOO.
................
After
................
..OOOOO.OOOOOOOO
..OO..OOOOOOOOO.
..OO..OOOO..OOOO
..OOOOOOOO..OOO.
..OOOOOOOOOOOOOO
................
Possible output
................
.>>>>>v.>>>>>>>.
..OO..>>^>>>>>v.
.>>v..OOO^..OO>.
..O>>>>>>^..OOO.
.>>>>>>>>>>>>>>.
................
Before
..............................
.OOO.O.O.....O.....O.O.O..O...
..OOO.O...O..OO..O..O.O.......
.....O......O..O.....O....O...
.O.OOOOO......O...O..O....O...
.OO..O..OO.O..OO..O..O....O...
..O.O.O......OO.OO..O..OO.....
..O....O..O.OO...OOO.OOO...O..
.....O..OO......O..O...OO.OO..
........O..O........OO.O.O....
..O.....OO.....OO.OO.......O..
.O.....O.O..OO.OO....O......O.
..O..OOOO..O....OO..........O.
.O..O...O.O....O..O....O...OO.
....O...OO..O.......O.O..OO...
........O.O....O.O....O.......
.OO.......O.OO..O.......O..O..
....O....O.O.O...OOO..O.O.OO..
.OO..OO...O.O.O.O.O...OO...O..
..............................
After
..............................
.OOOOO.......OO.....O..O......
...OO..O...O...O....OO....O...
....O.O......O..OO...OO...O...
.OO.OOOO......OO..O..O........
O.O.OO..O..O..O..OO...O...OO..
.OO.....O....OO.O..O.OO.O.....
......O.....O.....OOO.OO...O..
....O..OOOO..O..O..O.O.O.OO...
..O......O.O........O...O.O...
.O.....OOO.....OO.OO...O...O..
.......OOO..O.O.O...........O.
.O...O.....O...OOOO..O.O....O.
.O..O.O..O.....O......O....OO.
....O..O..O.O......O.....O....
........OOO....O......O..O....
.OO......O..OO..OOO.....O..O..
..O.O....OO..O...OO...O...OO..
.O..OO....O..O...O.O.O.OO.....
..............O............O..
Possible output
..............................
.OOO.O.v.....>.....>.v.O..v...
..>>^.v...>..^>..v..O.v.......
.....<......>..>.....O....O...
.O.<O><O......O...O..O....v...
.<O..O..v<.O..O^..O..>....>...
..<.^.v......OO.O^..>..<O.....
..^....v..v.Ov...>>^.<OO...O..
.....<..OO......O..O...Ov.v<..
........>..O........O^.v.^....
..^.....Ov.....OO.OO.......O..
.^.....^.^..O>.vO....v......O.
..<..Ov^^..O....><..........O.
.O..O...>.v....O..^....^...OO.
....O...<v..O.......<.^..v<...
........O.O....O.v....O.......
.OO.......<.Ov..O.......O..O..
....O....O.<.^...O^v..O.v.OO..
.O^..<<...O.>.v.>.^...<O...v..
..............................
fonte
>^<v
corresponde a um movimento de exatamente um passo na respectiva direção.Respostas:
Oitava,
494496 bytes - bônus de 372 bytes = 124 bytesAinda há muito o que fazer nesta resposta, mas eu queria ter a explicação não-destruída.
Eu vi isso como um problema de satisfação com restrições, então segui minha heurística de pesquisa local favorita, Min-conflitos . A ideia é, dada uma colocação inicial com cada germe em um destino acessível, selecione um germe aleatório que ocupe a mesma célula de destino que um ou mais outros germes e mova-o para uma célula válida que já tenha um mínimo de outros germes. Repita conforme necessário até que o canal corresponda à meta.
Curiosamente, não é garantido que esse algoritmo seja finalizado (se a meta for inacessível, ela continuará indefinidamente, por exemplo), mas se for finalizada, será garantida a produção de uma solução válida.
Aqui está o código:
Saída para o último caso de teste:
O tempo médio decorrido foi inferior a
9 segundos1 segundo * em um Core i5 de 5 anos, qualificado para o bônus.Estou tentando fazer isso funcionar com ideone, mas estou tendo o que acredito ser um escopo de problemas com a maneira como ele lida com funções aninhadas. (Aqui está o link ideone que não funciona) para referência: http://ideone.com/mQSwgZ )O código da ideone está funcionando agora. Eu tive que forçar todas as variáveis para global, o que era desnecessário executando localmente.
* Eu tinha uma nota na minha versão não destruída de que uma das etapas era ineficiente, então tentei ver se conseguia acelerar a execução e, para 2 bytes adicionais, o tempo de execução agora é de menos de um segundo. A saída de código e amostra foi atualizada e a entrada no ideone foi alterada para o último caso de teste.
fonte
Python, 1171 bytes - bônus de 878,25 bytes = 292,75 bytes
Link da Ideone: http://ideone.com/0Ylmwq
Leva de 1 a 8 segundos no último caso de teste, em média, qualificando-se para o bônus.
Este é o meu primeiro envio de código de golfe, portanto, provavelmente não é o programa com o melhor golfe por aí. No entanto, foi um desafio interessante e eu gostei bastante. O @Beaker merece uma menção por me lembrar que as pesquisas baseadas em heurísticas são uma coisa. Antes de ele postar sua solução e me inspirar a refazer a minha, minha pesquisa de força bruta era muito longa para se qualificar para o bônus no último caso de teste (era da ordem de 69! Iterações, que é um número de 99 dígitos. .).
Como não queria copiar diretamente a solução da Beaker, decidi usar o recozimento simulado para a heurística da minha pesquisa. Parece mais lento que o conflito mínimo para esse problema (provavelmente porque é um algoritmo de otimização, e não de satisfação com restrições), mas ainda está bem dentro da marca de 10 minutos. Também teve o benefício de ser bastante pequeno, em termos de código. Gastei muito mais bytes em transformar o problema do que em encontrar uma solução para ele.
Explicação
Minha solução é provavelmente ineficiente em termos de bytes, mas tive problemas para conceituar como resolver o problema como está e, portanto, acabei tendo que transformá-lo em um problema diferente que era mais fácil para mim entender. Percebi que existem quatro possibilidades para cada célula na grade:
Depois de decompor os dados nessas classes, consegui transformar ainda mais o problema. Foi imediatamente óbvio para mim que eu tinha que encontrar uma maneira de fornecer um germe do conjunto "antes, mas não depois" para uma célula no conjunto "depois, mas não antes". Além disso, os germes podem mover apenas um espaço; portanto, a única maneira de afetar as células mais distantes é "empurrando" um caminho ininterrupto de germes para dentro dessa célula. Isso significava que o problema era encontrar caminhos X separados por vértices em um gráfico, onde cada célula com um germe era um vértice no referido gráfico, e as arestas representavam células adjacentes.
Eu resolvi esse problema que, construindo primeiro o gráfico explicado acima. Enumerei todos os caminhos possíveis de cada célula no Antes e de cada célula no Depois, depois designei aleatoriamente cada célula no Antes de um de seus caminhos possíveis. Por fim, usei o recozimento simulado para alterar a solução potencial de forma semi-aleatória, até encontrar uma solução que não tem conflitos para nenhum dos caminhos.
Versão anotada
fonte