¿Cuál es tu problema de programación dinámica favorito?

Deben ser las subsecuencias de los corchetes de Andrew Stankevich Contest 17. Aprendí mucho sobre ese problema y todavía me encanta.

La declaración es muy simple:

Dada una secuencia de hasta 300 ‘(‘ sy ‘)’, su tarea es encontrar el número de sus diferentes subsecuencias que son secuencias de corchetes regulares. Por ejemplo, la secuencia “((()) ()) (” tiene 8 subsecuencias: “((()) ())”, “(()) ()”, “(((()))”, “(() ())”, “(())”, “() ()”, “()” Y “”.

Una vez que lees la declaración, crees que la tarea es simple. “Oh, está bien, este es el problema estándar de DP, para cada longitud de prefijo y cada número de paréntesis abiertos contaré las formas de terminarlo correctamente”.

Luego, lees el ejemplo una vez más y te das cuenta de que entendiste mal la tarea. “Espera, ¿qué? ¿Solo contamos cada subsecuencia una vez, incluso si ocurre en varios lugares? ¡Oh, mierda!”

Ahora viene la fase de desesperación. “OMG, ¿es eso incluso solucionable? ¡Hasta 20 caracteres podría aplicar fuerza bruta, pero la entrada tiene 300 de ellos!”

Finalmente, si tienes suerte, llega la iluminación.

Intente resolverlo por su cuenta 🙂 La tarea se puede encontrar en el gimnasio CodeForces, por ejemplo, si desea enviar su código. Publicaré una buena solución como comentario más tarde.

Editar: (¡Alerta de spoiler!) Solución para subsecuencias de corchetes por Michal Forišek en publicaciones

Mi problema favorito era de un concurso aleatorio:
Hay [math] n [/ math] aldeas en una línea. La distancia desde el pueblo más a la izquierda al pueblo [matemáticas] i [/ matemáticas] es [matemáticas] D_i [/ ​​matemáticas]. Desea construir menos de [math] K [/ math] bases entre las aldeas [math] n [/ math]. El costo de construir una base en el pueblo [matemáticas] i [/ matemáticas] es $ [matemáticas] C_i [/ ​​matemáticas]. Para el pueblo [matemáticas] i [/ matemáticas], si no existe una base dentro de una distancia de [matemáticas] S_i [/ ​​matemáticas], entonces le costará $ [matemáticas] w_i [/ ​​matemáticas] adicionales como compensación .
Su tarea es determinar dónde construir bases para minimizar el costo total.

Tenga en cuenta que [math] K \ leq 100, N \ leq 20000 [/ math]
Creo que es un problema bastante bueno. después de descubrir algunas buenas propiedades, puede resolverlo con algunas búsquedas binarias y un árbol de segmentos 🙂

Cuente el número de recorridos hamiltonianos en una cuadrícula hexadecimal con obstáculos (bromas (bueno, más o menos (las vacas deben ser advertidas))

Mi favorito con diferencia es este problema de CTSC 09 escrito por ahyangyi

Tiene 3 clavijas etiquetadas como 1 ~ 3 y n fichas marcadas como 1 ~ 3, todas inicialmente ubicadas en la clavija 1.

En cada movimiento, puedes quitar la ficha superior de una clavija y ponerla encima de otra clavija. (básicamente los movimientos que puedes hacer en las torres de Hanoi, excepto que no hay restricciones para que las fichas estén una encima de la otra)

Encuentre el número mínimo de movimientos para mover todos los 1s a la clavija 1, 2s a la clavija 2, 3s a la clavija 3.

La solución prevista es un [matemático] O (n ^ 2) [/ matemático] DP, 1 persona lo obtuvo durante el concurso. Hecho aún más ridículo: cuanto más pienses en este problema, menos código escribirás. Al final del día hay una solución DP de 40 líneas, O (n) tiempo para esto.

A2A.

Mis favoritos son EllysRivers de SRM 543 Div 1 (Declaración del problema) y camiseta de Codeforces CROC Champ 2012 Final round (Problema – D – Codeforces). Al principio, ambos parecen problemas clásicos de tipo mochila, pero los enfoques habituales claramente expirarían. Para resolverlos, debes hacer algunas buenas observaciones que me sorprendieron mucho de su elegancia después de descubrirlos.

Pruébalo si estás interesado. Lo primero es más fácil y puede darte una pista para hacer lo último 🙂

Gracias por una buena pregunta!

Mis favoritos son aquellos que tienen buena complejidad de tiempo gracias al análisis de costos amortizados.

Aquí hay uno: dado un texto, encuentre el borde más largo (que es un prefijo que también aparece como sufijo) que tiene al menos tres ocurrencias disjuntas.
Puedes oler KMP desde la distancia allí. Pero, ¿puede aplicar para resolver el problema en [math] \ mathcal {O} (N) [/ math] time? Puedes probarlo aquí: https://codility.com/programmers

Si quieres algo más difícil, aquí hay otro:
Dada una matriz [matemática] N \ veces N [/ matemática] de enteros positivos y un número [matemático] K> 0, [/ matemático] encuentra un rectángulo en la matriz, que la suma de elementos en el rectángulo está entre [ matemáticas] K [/ matemáticas] y [matemáticas] 2 \ cdot K [/ matemáticas].
Al principio parece un problema estándar encontrar un rectángulo con una suma dada de elementos. Las técnicas de escaneo de posibles rectángulos ya son algoritmos DP muy buenos. Pero le darán una solución ejecutándose en [math] \ mathcal {O} (N ^ 3) [/ math] time.
Resulta que el rango de [matemática] K [/ matemática] a [matemática] 2 \ cdot K [/ matemática] permite mejorar la complejidad del tiempo a [matemática] \ matemática {O} (N ^ 2) [/ matemática] .
Puedes probar esta tarea aquí: http://main.edu.pl/en/archive/oi

Me apasiona especialmente resolver los problemas de Digit DP .
Este no es seguramente uno de los más difíciles en la categoría.
Pero, es uno de mis favoritos.

Me encanta su idea, y ahora puedo hacerlo aún más difícil agregando algunas restricciones adicionales. Espero que disfrutes resolviéndolo.

Si te quedas atascado en eso, puedo ayudarte aquí en la sección de comentarios brindando ciertos consejos, pero dedícale tiempo.

Aqui esta el link.
SPOJ.com – Problema BALNUM

¡Feliz resolución!

Algoritmo de Floyd Warshall: todo el camino más corto de origen.

Son solo 4 líneas de código: tres bucles anidados y otra línea para actualizar la distancia más corta. ¡Implementarlo es así de simple! Me sorprendió la primera vez que lo leí en CLRS y Geeksforgeeks.

Solución de programación dinámica para el problema del camino hamiltoniano. (En general, DP con máscaras de bits)

Me encanta DP con bitmasking. Es difícil de implementar y el código al final sería muy sucio. A pesar de eso me siento bastante bien.

Otro problema que recuerdo bien es el último en 2016 Morgan Stanley Codeathon. Lo resolví con máscaras de bits y DP. El código es desordenado y estoy seguro de que no lo entenderé si lo revisto hoy. Solución rápida y sucia con demasiados bloques para y si los bloques anidados uno dentro del otro. ¡Estaba extremadamente feliz cuando recibí la marca verde!

Dada una máquina de estado finita determinista binaria arbitraria, ¿cuántas cadenas binarias de longitud n serán aceptadas por ella?

La idea parece complicada, pero el concepto es bastante simple.

Apuesto a que si tuviera media hora con algunas tazas y M&M podría explicárselo a un niño de 6 años.

Realmente me gustó este porque tuve que pasar rápidamente por este tipo de evolución de pensamiento para llegar a la solución.

Primero tuve que descubrir qué demonios era un DFSM binario.

Básicamente son solo diagramas de flujo como este:

Usted pasa una cadena de dígitos (digamos 10101101), que comienzan en el estado 0 y, en función de cada dígito posterior, mueven la posición actual a un estado diferente (o el mismo).

Básicamente solo toma una cuerda y sigue las flechas un dígito a la vez. Estas cadenas pueden ser bastante largas, aunque creo que el problema estableció un límite en 50 estados.

¿Ves cómo S0 tiene 2 círculos a su alrededor? Eso se llama un estado “aceptado”.

Si su cadena termina en uno de esos estados aceptados, entonces su cadena se considera aceptada. De lo contrario, se niega .

Entonces, la pregunta era básicamente preguntar: para todas las cadenas de 1s y 0s de longitud n, ¿cuántas de ellas serán aceptadas?

Entonces, como para n = 2, los estados serían 00, 01, 10 y 11. ¿Cuántos serían aceptados?

(Sugerencia: en la imagen de arriba, la respuesta sería 2 de 4).

La razón por la que es difícil es porque en este problema, n podría ser de hasta 10,000.

2 ^ 10,000 estados es … bueno, digamos que es un gran número.

Entonces, podría haber muchos estados aceptados, por lo que nos hicieron aplicar un módulo de 10 ^ 9 + 7 para reducirlo.

Spoilers más allá de este punto si estás interesado … es un problema muy divertido.


Así que comencé realmente simple con algo de fuerza bruta …

Como, eh … déjame hacer una función que evalúe aceptado o rechazado para cualquier cadena dada y devuelva verdadero o falso.

Luego repase todos los números y devuelva la respuesta.

Comencé a implementar esto e inmediatamente me di cuenta de que la cantidad de cálculos requeridos sería ridícula y fuera del alcance de la viabilidad, así que me detuve.


Luego lo pensé como un árbol o una estructura gráfica tradicional.

Puede hacer una búsqueda amplia en estos … Así que preparé una implementación rápida de BFS con una cola …

Básicamente, en cada estado se dividiría en ambas direcciones posibles hasta que se agotara la longitud restante, y luego verificaría si ese punto final era un estado aceptado o no.

Esto no fue tan tonto como el anterior, porque ahora no necesitaría construir + evaluar cada cadena desde cero, y fue una mejora en cómo pensé en atravesar la estructura.

Pero, en última instancia, aún se vio frenado por la misma explosión de cheques, por lo que, por supuesto, se agotó el tiempo para un n muy pequeño.

Creo que mi plan había sido memorizarlo de alguna manera, pero no era obvio y ya se sentía demasiado complicado.


Luego me di cuenta de que era un problema de programación dinámica, y se formó una solución casi final:

  • Cree una matriz de recuentos de estados que contenga el número de cadenas que actualmente terminan en un estado determinado.
  • Comience con 1 en el estado inicial.
  • Bucle n veces.
  • Cada vez, para cada estado de la matriz, agregue el recuento de estado actual a sus siguientes 0 y 1 estados.

Entonces, si comenzamos con 1 en el estado 0 y se ramifica a los estados 2 y 3, agregamos ese 1 a ambos estados 2 y 3.

En este punto me encontré con un problema: los datos del estado anterior todavía estaban allí.

En otras palabras, no movía los recuentos de estado a estado, los duplicaba . Esto condujo a una explosión incorrecta en los recuentos.

¿Cómo podría deshacerme de los datos antiguos de un recuento de estados y, al mismo tiempo, diferenciarlos de los datos que los estados anteriores acababan de agregar en la ronda actual?


Finalmente, me di cuenta de que todo lo que tenía que hacer era crear una nueva matriz donde los resultados fueran intercambiados en cada iteración.

Así que lo último funcionó así:

  • Bucle n veces
  • Para todos los estados, agregue su conteo actual a sus siguientes ubicaciones de estado 0 y 1 en una nueva matriz
  • Reemplace la matriz anterior con la nueva matriz
  • Modula todo para mantenerlo por debajo del límite
  • Sume todos los recuentos en los estados aceptados y regrese (asegurándose de aplicar el módulo a medida que agreguemos)
  • Y eso fue todo.

    Realmente simple cuando miras el resultado final, pero nunca antes había hecho ese tipo de intercambio delta.

    Muchos problemas son simplemente regurgitaciones aburridas de las mismas cosas viejas, pero este requería que descubriera una nueva forma de resolver algo que lo hizo divertido.

    Categorías de problemas favoritos

    1. Un problema que me hace darme cuenta de lo poco que sé sobre algo que pensé que sabía muy bien. Problema – 295B – Codeforces es un buen ejemplo de eso.

    2. Problemas que me hacen darme cuenta con qué facilidad se puede resolver un problema aparentemente desalentador (esta lista es INDIVIDUAL). Problema – 358D – Codeforces viene a la mente.

    3. Problemas que me hacen apreciar a otras personas, porque el problema es una obra de arte, y los acomodadores merecen una palmada en la espalda. (Lamentablemente, no puedo recordar ningún problema en esta categoría en este momento. Agregaré el enlace del problema cuando lo recuerde)

    Siento que muchos algoritmos estándar de programación dinámica son simplemente increíbles. Sin embargo, no son difíciles de entender, se basan en una idea extremadamente inteligente que simplifica el problema de una manera que luego se puede resolver con solo unas pocas líneas de código elegantes.

    Personalmente, me encanta la idea detrás del algoritmo Floyd Warshall para encontrar todos los pares de rutas más cortas en un gráfico. Un enfoque inteligente que reduce las múltiples llamadas dijkstra a solo 3-4 líneas de código (como magia).

    Mi otro algoritmo de programación dinámica favorito es el algoritmo de tabla dispersa para consultas de rango mínimo / máximo. La idea de usar segmentos de tamaño logarítmico como subproblemas es realmente inteligente.

    El algoritmo Floyd-Warshall es muy elegante. El problema es encontrar las distancias de camino más cortas entre todos los pares de vértices en un gráfico. ¡Después de la inicialización, el algoritmo consta de solo 4 líneas de código!

    Para mí, es muy probable que sea el más común: LCS. Encontrar una subsecuencia común más larga fue el primer lugar donde conocí a DP. Se puede diseñar tanto en tabulación como en memorización y le ayuda a comprender el razonamiento detrás de elegir la mejor manera al avanzar mientras se resuelve el problema.

    También le enseña cómo se resuelven los subproblemas superpuestos sin repetición, que es la idea principal de DP.

    Mi problema DP favorito es “Bienvenido a CodeJam”. Fue mi primera introducción a DP y esto no se puede resolver con la fuerza bruta, incluso en la imaginación.

    Panel de control – Ronda de clasificación 2009 – Google Code Jam

    Si lees el párrafo anterior, probablemente te estés preguntando por qué está allí. Pero si lo lee con mucho cuidado, puede notar que hemos escrito las palabras “bienvenido al código jam” varias veces: 400263727 veces en total.

    Puntuación de juego

    El mío es Cookie Clicker Alpha. Mi primer DP que resolví

    Mi libro tiene alrededor de 20 de los principales problemas que se plantean en los concursos y entrevistas de codificación.

    Programación dinámica para codificar entrevistas (un enfoque ascendente para la resolución de problemas)

    Cambio de moneda DP, el primero que resolví 😛

    Subcadena palindrómica más larga … compruébelo desde aquí … Cómo prepararse para una entrevista – 10

    Mochila, mi primer DP.

    La subsecuencia común más larga es el problema de mi dp favorito.

    More Interesting

    ¿Cuáles son algunos algoritmos nuevos e interesantes en bioinformática / informática genómica?

    ¿Por qué el algoritmo Chandy-Lamport necesita suponer que todos los mensajes llegan exactamente una vez?

    ¿Qué técnicas eficientes ha intentado rastrear un algoritmo o un código de programa manualmente, sin usar una computadora?

    ¿Cuál es la lógica detrás de los números de una tarjeta de regalo?

    ¿Qué series matemáticas debo saber para calcular la complejidad de cualquier algoritmo o pseudocódigo?

    ¿Cuántos años se necesitan para aprender algoritmos de cero a héroe?

    ¿Cómo los algoritmos de programación dinámica son mejores que otros algoritmos?

    Cómo encontrar la suma de números naturales que suman N usando formularios y funciones en HTML

    Cómo calcular la velocidad de un algoritmo

    ¿Cuál es el mejor algoritmo de clasificación manual? Por ejemplo, si tuviera una pila de papeles que quisiera ordenar alfabéticamente, ¿cuál sería la forma más eficiente de hacerlo? ¿Qué pasaría si estuvieras de acuerdo con que uno o dos se alejen de su posición ordenada?

    Cómo encontrar la ruta más corta en un gráfico no ponderado en lenguaje C

    ¿Cuáles son los algoritmos de optimización más simples y fundamentales?

    Silicon Valley (serie de televisión): ¿Cuál es el ejemplo más cercano en la vida real al algoritmo de compresión de Pied Piper?

    ¿Es la calificación de revisión un factor en el algoritmo de 'Yelp Sort' de Yelp?

    ¿Existe un algoritmo de tiempo O (N) para esta pregunta?