Introducción
Usted es biólogo y estudia los patrones de movimiento de las bacterias. Su equipo de investigación tiene un montón de ellos en una placa de Petri, y usted está registrando su actividad. Desafortunadamente, no tiene fondos suficientes y no puede pagar una cámara de video, por lo que simplemente toma una foto del plato a intervalos regulares. Su tarea es hacer un programa que rastree los movimientos de los gérmenes a partir de estas imágenes.
Entrada
Sus entradas son dos matrices 2D de caracteres en cualquier formato razonable, que representan imágenes consecutivas de la placa de Petri. En ambas matrices, el carácter .
representa un espacio vacío y O
representa un germen (puede elegir dos caracteres distintos si lo desea). Además, la matriz "después" se obtiene de la matriz "antes" moviendo algunos gérmenes un paso en una de las cuatro direcciones cardinales; en particular, las matrices tienen la misma forma. Los gérmenes se mueven simultáneamente, por lo que uno de ellos puede moverse a un espacio que ya contenía otro germen, si se mueve fuera del camino. Se garantiza que los bordes de la matriz "antes" contienen solo espacios vacíos y que hay al menos un germen. Por lo tanto, el siguiente es un par válido de entradas:
Before After
...... ......
.O..O. ....O.
.OO.O. .OO.O.
...... ..O...
Salida
Su salida es una única matriz 2D de caracteres en el mismo formato que las entradas. Se obtiene de la matriz "antes" al reemplazar los gérmenes que se han movido con uno de ellos >^<v
, dependiendo de la dirección del movimiento (también puede usar 4 caracteres distintos aquí). Puede haber varias salidas posibles, pero debe dar solo una de ellas. En el ejemplo anterior, una salida correcta posible es
......
.v..O.
.>v.O.
......
Se permite un movimiento innecesario en la salida y los gérmenes pueden intercambiar lugares, por lo que lo siguiente también es válido:
......
.v..v.
.>v.^.
......
Reglas y puntaje
Puede escribir un programa completo o una función. El conteo de bytes más bajo gana, y las lagunas estándar no se permiten.
Estoy interesado en algoritmos relativamente eficientes, pero no quiero prohibir por completo la fuerza bruta. Por esta razón, hay una bonificación del -75% para resolver el último caso de prueba en 10 minutos en una CPU moderna (no puedo probar la mayoría de las soluciones, así que confiaré en ti aquí). Descargo de responsabilidad: sé que existe un algoritmo rápido (buscar "problema de rutas disjuntas"), pero no lo he implementado yo mismo.
Casos de prueba adicionales
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..
..............................
fuente
>^<v
corresponde a un movimiento de exactamente un paso en la dirección respectiva.Respuestas:
Octava,
494496 bytes - Bonificación de 372 bytes = 124 bytesTodavía hay mucho golf por hacer en esta respuesta, pero quería obtener la explicación sin fundamento.
Vi esto como un problema de satisfacción de restricciones, así que elegí mi heurística de búsqueda local favorita, Min-conflict . La idea es que, dada una ubicación inicial con cada germen en un destino accesible, seleccione un germen aleatorio que ocupe la misma celda de destino que uno o más de otros gérmenes y muévalo a una celda válida que ya tenga un mínimo de otros gérmenes. Repita según sea necesario hasta que la colocación coincida con el objetivo.
Curiosamente, no se garantiza que este algoritmo termine (si el objetivo es inalcanzable, continuará indefinidamente, por ejemplo), pero si termina, se garantiza que producirá una solución válida.
Aquí está el código:
Salida para el último caso de prueba:
El tiempo promedio transcurrido fue inferior a
9 segundos1 segundo * en un Core i5 de 5 años, calificando para el bono.Estoy tratando de hacer que esto funcione en ideone, pero estoy teniendo lo que creo que son problemas de alcance con la forma en que maneja las funciones anidadas. (Aquí está el enlace ideone que no funciona para referencia: http://ideone.com/mQSwgZ )El código en ideone ahora está funcionando. Tuve que forzar todas las variables a global, lo que era innecesario ejecutarlo localmente.
* Tuve una nota en mi versión no escrita de que uno de los pasos era ineficiente, así que lo intenté para ver si podía acelerar la ejecución y para 2 bytes adicionales el tiempo de ejecución ahora es menos de un segundo. El código y la salida de muestra se han actualizado y la entrada en ideone se ha cambiado al último caso de prueba.
fuente
Python, 1171 bytes - 878.25 bytes de bonificación = 292.75 bytes
Enlace de ideona: http://ideone.com/0Ylmwq
Toma de 1 a 8 segundos en promedio en el último caso de prueba, calificando para el bono.
Esta es mi primera presentación de código de golf, por lo que probablemente no sea el mejor programa de golf que existe. Sin embargo, fue un desafío interesante y lo disfruté bastante. @Beaker merece una mención por recordarme que las búsquedas basadas en heurística son una cosa. Antes de que publicara su solución y me inspirara a rehacer la mía, mi búsqueda de fuerza bruta fue muuuucho más larga para calificar para la bonificación en el último caso de prueba (¡fue del orden de 69 iteraciones, que es un número de 99 dígitos ... .).
No quería copiar directamente la solución de Beaker, así que decidí usar recocido simulado para mi búsqueda heurística. Parece más lento que el conflicto mínimo para este problema (probablemente porque es un algoritmo de optimización en lugar de uno de restricción-satisfacción), pero todavía está dentro de los 10 minutos. También tenía el beneficio de ser bastante pequeño, en cuanto a código. Gasté muchos más bytes en transformar el problema que en encontrar una solución.
Explicación
Probablemente mi solución sea bastante ineficiente en cuanto a bytes, pero tuve problemas para conceptualizar cómo resolver el problema tal como está y, por lo tanto, terminé teniendo que transformarlo en un problema diferente que me resultó más fácil de entender. Me di cuenta de que hay cuatro posibilidades para cada celda en la cuadrícula:
Después de descomponer los datos en esas clases, pude transformar aún más el problema. Para mí fue inmediatamente obvio que tenía que encontrar una manera de suministrar un germen del conjunto "antes pero no después" a una celda en el conjunto "después pero no antes". Además, los gérmenes solo pueden moverse un espacio, por lo que la única forma de que afecten a las células más alejadas es "empujando" una ruta ininterrumpida de gérmenes hacia esa célula. Eso significaba que el problema se convirtió en encontrar X caminos separados de vértices en un gráfico, donde cada celda con un germen era un vértice en dicho gráfico, y los bordes representaban celdas adyacentes.
Resolví ese problema que al construir primero el gráfico explicado anteriormente. Luego enumeré cada ruta posible desde cada celda en Antes y cada celda en Después, luego asigné aleatoriamente cada celda en Antes una de sus posibles rutas. Finalmente, utilicé el recocido simulado para mutar semialeatoriamente la solución potencial hasta que finalmente encuentro una solución que no tenga conflictos para ninguno de los caminos.
Versión anotada
fuente