Visitar puntos en una recta numérica mientras minimiza un costo no relacionado con la distancia


18

Necesito ayuda sobre este problema ACM ICPC. Mi idea actual es modelar esto como un problema de ruta más corta, que se describe en la declaración del problema.

Problema

Hay N = 1000contenedores de desechos nucleares ubicados a lo largo de una línea de números 1-D en posiciones distintas de -500,000 to 500,000, excepto x=0. Una persona tiene la tarea de recoger todos los contenedores de basura. Cada segundo que no se recoge un contenedor de residuos, emite 1 unidad de radiación. La persona comienza a la unidad x = 0y puede moverla 1cada segundo, y la recolección de los desechos lleva un tiempo insignificante. Queremos encontrar la cantidad mínima de radiación liberada mientras recolectamos todos los contenedores.

Entrada de muestra:

4Contenedores ubicados en [-12, -2, 3, 7].

El mejor orden para recolectar estos contenedores es [-2, 3, 7, -12], para un mínimo de emisión de 50unidades. Explicación: la persona acude -2en 2 segundos y durante ese tiempo 2 unitsse emite radiación. Luego va a 3(distancia:) 5para que el cañón haya liberado 2 + 5 = 7unidades de radiación. Tarda 4más segundos en llegar a x = 7donde ese barril ha emitido 2 + 5 + 4 = 11unidades. Toma 19segundos para llegar a x = -12donde ese barril ha emitido 2 + 5 + 4 + 19 = 30unidades. 2 + 7 + 11 + 30 = 50, cual es la respuesta.

Notas

Hay una O(N!)solución obvia . Sin embargo, he explorado métodos ambiciosos como pasar al más cercano o al clúster más cercano, pero no han funcionado.

He pensado en este problema durante bastante tiempo, y lo he modelado como un problema de búsqueda de gráficos:

  1. Lo insertamos 0como una posición de referencia (Este será el estado inicial)
  2. Luego, clasificamos las posiciones de menor a mayor.
  3. Luego hacemos un BFS / PFS, donde stateconsiste en
    • Dos enteros ly rque representan un rango contiguo en la matriz de posición ordenada que ya hemos visitado
    • Un entero locque nos dice si estamos en el punto final izquierdo o derecho del rango
    • Un número entero timeque nos dice el tiempo transcurrido.
    • Un 'costo' entero que nos dice el costo total hasta ahora (según los nodos que hemos visitado)
  4. Desde cada estado podemos movernos a [l - 1, r] y [l, r + 1], ajustando los otros 3 enteros en consecuencia
  5. El estado final es [0, N], verificando ambas posiciones finales.

Sin embargo, parece que [L, R, loc]no define de forma exclusiva un estado, y tenemos que almacenarlo L, R, loc, and time, minimizando costen cada uno de estos. Esto lleva a un algoritmo exponencial, que todavía es demasiado lento para ser bueno.

¿Alguien puede ayudarme a ampliar mi idea o empujarla en la dirección correcta?

Editar: ¿ Quizás esto se pueda modelar como un problema de optimización de programación dinámica? Pensando en ello, tiene los mismos problemas que la solución de búsqueda de gráficos: solo porque la corriente costes baja no significa que sea la respuesta óptima para ese subproblema, ya que timetambién afecta la respuesta en gran medida.

Codicioso no funciona: tengo un algoritmo de selección codicioso que estima el costo de mudarse a un lugar determinado (por ejemplo, si nos movemos a la derecha, duplicamos las distancias a los barriles izquierdos y tal).

¿Podría hacer una búsqueda de prioridad primero, con una heurística? La heurística podría combinar el costo del viaje actual con la cantidad de tiempo transcurrido.


¿Qué hay de los algoritmos de ruta más corta? ¿Te gusta el algoritmo de Dijkstra?
suraj_fale

Lo intenté, pero creo que estoy haciendo algo realmente mal. Describí mi algoritmo (que era la primera búsqueda de prioridad o BFS) cerca de la parte inferior, con la lista numerada.
Barron W.


Lo siento, no veo cómo se relacionan estos dos problemas. ¿Puedes explicar?
Barron W.

2
Este es un problema de práctica ACM ICPC, no un problema de la vida real. En otra nota, intenté reducir el estado pero fue en vano. Intenté escribir una solución DP pero eso tampoco funcionó. El estado era L, R, POS.
Barron W.

Respuestas:


4

Creo que puede mejorar esto al ver el problema como un gráfico dirigido de pares de posiciones.

Para este ejemplo, usaré la línea con los valores -9, -6, -1, 3 y 5.

Debido a que es demasiado difícil dibujar un gráfico con solo texto, voy a representar los pares como una tabla. Podemos pensar que las celdas representan el estado donde se han recolectado todos los contenedores entre esas dos posiciones. Cada celda tiene dos valores, uno que representa el costo para ir a la izquierda y el otro que representa el costo para ir a la derecha (al siguiente contenedor).

Los valores pueden calcularse simplemente como la distancia entre los dos puntos multiplicada por el número de barriles fuera de esos dos puntos + 1 . Las celdas donde ambos números tienen el mismo signo representan casos en los que se han recolectado todos los barriles del signo opuesto. Estos se calculan utilizando solo el número de barriles en la dirección alejada de cero.

En la tabla, los valores de X significan que no puede ir en esa dirección (se han tomado todos los barriles en esa dirección). Las filas representan la posición actual del colector y las columnas representan la ubicación del próximo barril opuesto.

    +------+------+------+------+------+
    |  -9  |  -6  |  -1  |   3  |   5  |
+---+------+------+------+------+------+
|-9 |      |      |      | X, 24| X, 14|
+---+------+------+------+------+------+
|-6 | 3, X |      |      | 9, 27| 6, 22|
+---+------+------+------+------+------+
|-1 |      |10, X |      |20, 8 |15, 18|
+---+------+------+------+------+------+
| 3 |24, 4 |27, 6 |16, 8 |      | X, 2 |
+---+------+------+------+------+------+
| 5 |14, X |22, X |18, X |      |      |
+---+------+------+------+------+------+

Las reglas para moverse entre cuadrados pueden ser algo confusas.

Para columnas numeradas negativas, las reglas son las siguientes:

  • Ir a la derecha baja una celda.
  • Ir a la izquierda se mueve hacia abajo una celda y luego se refleja en la diagonal.
  • Si el valor correcto es X, ir a la izquierda se mueve hacia la diagonal (donde la columna y la fila son iguales) y hacia la izquierda por una.

Para columnas numeradas positivas, las reglas son las siguientes:

  • Ir a la izquierda mueve una celda hacia arriba.
  • Ir a la derecha se mueve hacia arriba una celda y luego se refleja en la diagonal.
  • Si el valor izquierdo es X, ir a la derecha se mueve hacia abajo a la diagonal y a la derecha en uno.

Ahora podemos ejecutar el algoritmo de Dijkstra para calcular la mejor ruta, usando estas reglas de movimiento para atravesar el gráfico. Nuestras posiciones iniciales son (-1, 3) y (3, -1) con costos iniciales de 5 y 15, respectivamente. Una vez que hemos calculado los valores para las dos posiciones finales (a la izquierda de (-9, -9) y a la derecha de (5, 5)), la más pequeña de las dos es nuestra respuesta.

El tiempo de ejecución para cada uno de los pasos son:

  • O (N log N) para ordenar inicialmente los valores de entrada a lo largo de la línea
  • O (N ^ 2) para calcular la tabla / gráfico
  • O (N ^ 2 log N) para ejecutar Dijkstra's en el gráfico (Nota: hay como máximo dos aristas para cualquier vértice dado).

Los primeros dos pasos están dominados por el último, por lo que su tiempo de ejecución general es O (N ^ 2 log N), que debería ser un tiempo de ejecución lo suficientemente bueno para el desafío.


1

Distancia más corta

Ayer escribí una aplicación Java para resolver el problema. El problema es básicamente un problema de distancia más corta, como dijo SRJ en su comentario. La radiación solo muestra que puede acumular un valor junto con la distancia más corta.

Básicamente, esto es lo que hice.

  • Ponga los números de contenedor en una lista <Integer>
  • Mientras que la lista contiene elementos;
    • Encuentra los elementos más cercanos
    • Si hay un elemento, camine hasta allí y elimine el elemento.
    • Si hay dos elementos, copie la ruta y camine a ambos elementos
  • Encuentre el camino con el menor valor de radiación.

Aquí hay algunos resultados de la aplicación

10 containers are located at [-90, -75, -47, -9, 9, 26, 48, 50, 64, 79].

You walk to position -9 and pick up the container.  The total radiation emitted is 90 units.
You walk to position 9 and pick up the container.  The total radiation emitted is 252 units.
You walk to position 26 and pick up the container.  The total radiation emitted is 388 units.
You walk to position 48 and pick up the container.  The total radiation emitted is 542 units.
You walk to position 50 and pick up the container.  The total radiation emitted is 554 units.
You walk to position 64 and pick up the container.  The total radiation emitted is 624 units.
You walk to position 79 and pick up the container.  The total radiation emitted is 684 units.
You walk to position -47 and pick up the container.  The total radiation emitted is 1,062 units.
You walk to position -75 and pick up the container.  The total radiation emitted is 1,118 units.
You walk to position -90 and pick up the container.  The total radiation emitted is 1,133 units.

No optimicé el algoritmo de ninguna manera. Ni siquiera me di cuenta de que la lista de entrada de números estaba ordenada. (Soy un tonto)

Cuando ejecuté mi código con los valores máximos, 1,000 contenedores y un rango de -500,000 a 500,000, mi código tardó 3 segundos en ejecutarse. La mayor parte de ese tiempo fue escribir las 1,000 líneas de impresión en la consola.

No soy una gran persona O, pero creo que mi fuerza bruta que recorre el algoritmo de ruta más corta es O (N al cuadrado), no O (N!).

Si aproveché el hecho de que los números de entrada están ordenados, de modo que solo tuve que verificar los dos números a cada lado de donde estoy ubicado actualmente, podría obtener la aplicación cerca de O (N). Solo tengo que verificar 2 números porque estoy eliminando los elementos de la Lista a medida que los obtengo.

Usó diferentes algoritmos, como el algoritmo codicioso, para encontrar una solución aproximada.

Si mi programa hubiera tardado 3 horas en ejecutarse en lugar de 3 segundos, entonces tendría que tomar una decisión.

¿Es la solución suficientemente buena lo suficientemente buena?

En otras palabras, ¿estoy dispuesto a cambiar la velocidad de procesamiento por una respuesta lo suficientemente buena?

Si una respuesta lo suficientemente buena es lo suficientemente buena, entonces usa algoritmos de aproximación.

Si quieres la respuesta perfecta, tienes que recorrer todos los caminos más cortos. No hay ningún atajo.

Si alguien quiere que publique mi código, lo haré. Estoy seguro de que todavía hay errores, ya que quería ver si podía implementar un algoritmo de caminata más corto.


1

Tengo una solución que resolverá este problema a 2^Ntiempo, que es pobre, pero creo que es una forma útil de enmarcar el problema, así que pensé en publicar.

En lugar de modelar el problema como un gráfico, modelaría que es un árbol de decisión binario (digamos T). En cada nivel tienes que elegir entre ir a la derecha o a la izquierda. Es bastante fácil calcular el "costo" de cada borde. Sea h(K)la altura del nodo actual K, luego el costo del borde que va a left_child(K) = h(K) x dist(K, left_child(K)). Un cálculo similar es suficiente para el costo del borde para el niño correcto. Construye este árbol y realiza un seguimiento del costo acumulativo de los bordes hasta el final, e informa la ruta al nodo hoja con el costo total más pequeño.

Tenga en cuenta que el cálculo del costo funciona porque la longitud de cada borde dist(K, left_child(K))representa el tiempo para ir al siguiente sitio, mientras que la altura del subárbol es el número de sitios restantes (por ejemplo, todavía emitiendo radiación).

Ahora, el truco dentro de este marco es determinar si hay algunas heurísticas que puede usar para 'probar' que puede ignorar expandir la búsqueda a lo largo de alguna rama. Mi intuición es que para cualquier tipo de heurística existe una disposición de sitios que lo derrotará, pero quizás alguien pueda llegar a algo.

Varios han propuesto aplicar soluciones de ruta más corta para gráficos, tengo algunas dudas sobre si dicha solución puede funcionar. Sus vecinos en el 'gráfico' en el problema cambiarán dependiendo de la ruta que siga. Por ejemplo, en su publicación original con [-12, -2, 3, 7]si va a -2entonces -12y se 3convierte en 'vecinos' y si va a 3entonces -2y 7son vecinos. Cada posible 'par' de valores positivos y negativos puede ser potencialmente vecino en el gráfico final. No conozco ningún algoritmo de ruta más corta que sea demostrablemente correcto en un gráfico dinámico.


0

Creo que tiene más sentido pensar en cada etapa simplemente como una elección binaria entre ir directamente al barril más cercano e ir a la izquierda al barril más cercano. Simplemente tenga una función de costo que detalla el número de unidades de radiación en las que se incurriría en total al hacer cualquier movimiento y elija la que tenga el costo más bajo.

NO considere simplemente el barril más cercano, sino que suponga que al alejarse de un barril está agregando efectivamente el doble de radiación porque al alejarse también ha incurrido en el costo de tener que retroceder en esa dirección más tarde.

En su ejemplo de [-12, -2,3,7], moverse hacia la izquierda generaría un total de 14 (2 + 2 + 10) a la izquierda y 18 (2 + 2 + 5 + 9) a la derecha, para un total de 22. Mover a la derecha incurriría en 10 (3 + 3 + 4) a la derecha y 26 (3 + 3 + 5 + 15) a la derecha. Claramente a la izquierda es la solución correcta al principio. Se puede hacer un cálculo similar para cada movimiento sucesivo.

Después de eso, el problema básicamente se reduce a una búsqueda, por lo que la complejidad debería ser O (nlog (n)), que es MUCHO mejor que O (n!). Creo que esta es necesariamente la complejidad más baja que puede existir para este problema, ya que es básicamente un algoritmo de búsqueda basado en comparación para el que no es posible hacerlo mejor que O (nlog (n))

Aparentemente no estaba lo suficientemente claro con esta descripción, así que decidí hacerlo un poco más programático: 1. Calcule el costo incurrido yendo hacia la izquierda y el costo incurrido yendo hacia la derecha en función de la posición actual 2. Muévase la dirección menos costosa 3. Retire el barril alcanzado de la consideración al calcular el costo de moverse en una dirección

Cálculo del costo: 1. Identifique el barril más cercano en la dirección dada. Digamos que $ dist es la distancia desde la posición actual hasta el barril más cercano en la dirección dada. 2. El costo se inicializa como N * $ dist donde N solo considera barriles aún activos. 3. A esto, agregue la distancia que la nueva posición indicada por $ dist sería de cada barril restante.


1
Esto no siempre funciona. Tal vez podría ordenar las coordenadas y luego hacer una búsqueda donde el estado contiene un rango [i..j] (que significa qué rango ha visitado) y el costo y la hora actual.
Barron W.

¿Cuándo no funciona esto?
Slater Victoroff

Hubo un caso de prueba simple donde esto falló. Intentaré encontrarlo, pero fue con N = 4 o 5.
Barron W.

[43, -18, -98, -82, 63]
Barron W.

También casos como [-10,-11, 10,20,30,40,50,60,70]. La solución correcta y única es recolectar todos los negativos y luego recolectar los positivos. Para una respuesta de 455.
Barron W.

0

Solución parcial: volveré a ello más tarde.

Suponga que la estrategia "predeterminada" se ejecuta completamente hacia la izquierda o hacia la derecha, lo que sea más barato. Ahora pregunte, ¿vale la pena hacer un pequeño viaje de ida para recoger un barril? Es bastante fácil calcular la respuesta.

Para su entrada de muestra, ejecutar todo a la derecha es más barato que a la izquierda. ¿Va a -2 vale la pena un viaje lateral? Reduce el costo de correr completamente hacia la derecha y luego vuelve a 0 en 14 (porque estaba "pagando" 4 unidades de radiación por movimiento de 0 a 3 en la estrategia predeterminada, ahora está en 3, estaba pagando 3 de 3 a 7, ahora es 2, etc.), además reduce en uno por movimiento su costo de mover de 0 a -2, ahorrando así 2 más para un total de 16.

Sin embargo, agrega un costo de ir a -2 y luego volver a 0 de 14 (4 unidades por movimiento a -2, 3 por movimiento a 0), para una ganancia neta de (16-14) = 2. Tenga en cuenta que para calcular esto no tiene que evaluar el costo exacto de resolver el problema completo para cada decisión: tiene suficiente información para decidir simplemente sabiendo si correr todo el camino hacia la izquierda es más barato que correr hacia la derecha, más cómo muchos contenedores de desechos están a cada lado de usted, y la distancia al 2 más cercano. Entonces eso es O (N ^ 2).

Excepto por un problema importante: he asumido que correrás hasta el final, y sabemos que podrías duplicarlo. Para limpiar eso, necesitamos actualizar mi cálculo. Para la entrada de muestra, supuse que ahorraría 14 al emitir 1 radiación total menos por unidad por segundo mientras se ejecuta de 0 a 7 y viceversa. Pero, si duplica antes de correr a 7, los ahorros se reducirán.

Eso es bastante malo, porque no sé cómo calcular la próxima doble vuelta sin probar todas las posibilidades, lo que nos vuelve a poner en O (2 ^ N).

Excepto: son 2 ^ N con poda. Calculé que el "viaje lateral" a -2 costaba 14, pero gané 16, si no tenía más viajes laterales antes de llegar al número más a la derecha. Si el número más a la derecha hubiera sido 5, sabría de inmediato que el viaje lateral a -2 no podría pagar. (Costo aún 14, beneficio máximo 12). Tampoco tengo que considerar ir a -2 y luego hacer un viaje lateral antes de llegar a 6, ya que eso siempre es inferior a ir directamente a ese punto en primer lugar.


0

Creo que puede resolverlo usando una búsqueda de amplitud, manteniendo no más de 2 * N ^ 2 tuplas de (boolean, int, int, int, string) donde las cadenas son tan largas como la ruta es complicada.

Las tuplas son (booleano mínimo o máximo, posición mínima viajada, posición máxima viajada, radiación total emitida, historial de trayectoria).

Veo el algoritmo así:

  1. Inicialice el grupo de tuplas en una sola entrada, (min, 0, 0, 0, "")
  2. Encuentre un elemento en la piscina que tenga una radiación mínima emitida. Si el mínimo y el máximo corresponden al mínimo y al máximo de todos los barriles, entonces el historial de la ruta es la solución óptima. De lo contrario, bórrelo del grupo.
  3. Calcule los 2 descendientes de esta tupla, cada uno de los cuales corresponde a caminar hacia la izquierda o hacia la derecha hasta el siguiente barril sin procesar.
  4. Inserte los descendientes en la piscina. Si ya hay un elemento en la piscina con el mismo valor booleano, mínimo y máximo que un nuevo descendiente, descarte el elemento con el mayor recuento de radiación.
  5. ir a 2.

Encontrar y eliminar tuplas dominadas mejorará drásticamente el rendimiento. Puede valer la pena agregar una bandera 'ha criado "a cada tupla y dejar las tuplas criadas en el grupo.

También hay que tomar algunas decisiones importantes al decidir cómo almacenar las tuplas y buscar dominios y nuevos elementos para reproducirse.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.