Cómo aumentar mis habilidades en programación dinámica

La idea básica de la programación dinámica es bastante directa. Para ser bueno en DP, esencialmente necesita sentirse cómodo con esta idea, y luego se trata de practicar. Daré algunas técnicas explicando cómo se te ocurre una solución DP para algunos problemas clásicos de DP:

La idea básica es que necesita encontrar un subproblema con una estructura similar al problema que está tratando de resolver. Por lo general, no hay muchas maneras de hacerlo, y solo teniendo en cuenta todas las posibilidades le da la solución.

Veamos algunos ejemplos:

  • Problema de cambio de moneda: dado un valor [matemático] N [/ matemático], si queremos hacer un cambio para [matemático] N [/ matemático] centavos, y tenemos un suministro infinito de cada uno de [matemático] S = {S_1, S_2 , .., S_m} [/ math] monedas valoradas, ¿de cuántas maneras podemos hacer el cambio? El orden de las monedas no importa.

¿Cómo procedes? Claramente, el problema tiene dos dimensiones: el valor [matemática] N [/ matemática] y los tipos de monedas. Si [math] N [/ math] fuera pequeño como 1, o hubiera un solo tipo de moneda, la respuesta sería trivial. Por lo tanto, sus subproblemas tendrán una [matemática] N [/ matemática] más pequeña o menos tipos de monedas, o ambas.

¿Cómo sabes cuál es? Consideremos el caso de las [matemáticas] N [/ matemáticas] más pequeñas. Digamos que la [matemática] N [/ matemática] dada fue 10. Ahora, si supiera de cuántas maneras puede hacer un cambio para un valor menor, digamos 8, puede encontrar eficientemente el no. de maneras de hacer cambios para 10? Tenga en cuenta que la palabra clave aquí es “eficientemente”: si no puede hacerlo de manera eficiente, DP es inútil. Ahora, usted extiende la pregunta anterior: si supiera de cuántas maneras puede hacer cambios para todos los valores de [math] N [/ math] más pequeños que 10, puede encontrar eficientemente el no. de maneras de hacer cambios para 10? Una forma en que cree que puede proceder es agregar el no. de formas de hacer cambios para [math] 10-S_1 [/ math], no. de formas de hacer cambios para [math] 10-S_2 [/ math],…, porque agregar una moneda a cada una de estas formas le daría una suma de 10. Sin embargo, esto es incorrecto, porque el orden de las monedas no es importante , entonces estarás contando en exceso. En particular, si [matemática] S_1 = 1 [/ matemática] y [matemática] S_2 = 2 [/ matemática], entonces [matemática] 10 – S_1 [/ matemática] incluye formas que incluyen monedas de denominación 2, y usted agregue 1 a estos para obtener 10. De manera similar, [matemática] 10 – S_2 [/ matemática] incluye formas que incluyen monedas de denominación 1, y agregará 2 a estas para obtener 10. Entonces hay un recuento repetido.

Vayamos a la siguiente dimensión: los tipos de monedas. Suponga que sabe cuántas formas hay de cambiar [matemáticas] N [/ matemáticas] usando monedas de denominaciones [matemáticas] {S_1, …, S_ {m-1}} [/ matemáticas]. ¿Puedes usar esto para generar el no. de maneras de hacer un cambio de N usando [math] {S_1, …, S_m} [/ math]? Claramente no.

Pero, si sabía cómo hacer un cambio para cualquier valor por debajo de [matemática] N [/ matemática] utilizando monedas [matemática] {S_1, …, S_ {m-1}} [/ matemática], puede obtener la respuesta. En particular, no. de formas de hacer cambios para [matemática] N [/ matemática] usando monedas [matemática] {S_1,…, S_m} [/ matemática] = no. de formas de hacer cambios para [math] N – S_m [/ math] usando monedas [math] {S_1,…, S_m} [/ math] + no. de formas de hacer cambios para [matemáticas] N [/ matemáticas] usando monedas [matemáticas] {S_1, …, S_ {m-1}} [/ matemáticas]. Tenga en cuenta que los dos términos anteriores son mutuamente excluyentes: el primer término corresponde a los casos en los que utiliza al menos una moneda de denominación [matemáticas] S_m [/ matemáticas], mientras que el segundo caso no incluye monedas de denominaciones [matemáticas] S_m [/ matemáticas].

Ahora, desde este punto, solo tiene que realizar el llenado de la tabla para obtener la respuesta.

  • Problema de la ruta más corta de todos los pares: dado un gráfico, encuentre la ruta más corta desde cada vértice a cualquier otro vértice.

¿Qué puede reducir para reducir el tamaño del problema? El tamaño del gráfico, así que encuentre la ruta más corta desde el vértice i al j usando el subgrafo [math] {v_1, …, v_k} [/ math]. Y eso te da el algoritmo Floyd-Warshall.

  • Problema de suma de subconjunto: dado un conjunto de enteros no negativos [matemática] S = {s_1,…, s_m} [/ matemática] y un valor [matemático] N [/ matemática], determine si hay un subconjunto de establecer [matemáticas] S [/ matemáticas] con una suma igual a [matemáticas] N [/ matemáticas].

Nuevamente, hay dos dimensiones: el tamaño del conjunto y [math] N [/ math]. Procediendo de manera similar al problema de cambio de monedas, debe usar subproblemas en ambas dimensiones: hay una manera de hacer [math] N [/ math] si hay una manera de hacer [math] N – s_m [/ math] usando [ matemática] {s_1, …, s_ {m-1}} [/ matemática], o si hay una manera de hacer [matemática] N [/ matemática] usando [matemática] {s_1, …, s_m} [/ matemática] . Esto tiene la misma interpretación que arriba: o [math] s_m [/ math] está incluido en la suma o no lo está.

A menudo, las personas comienzan pensando en estos casos mutuamente excluyentes, como la inclusión y la exclusión, lo que hace que sea complicado porque diferentes problemas tienen diferentes criterios para generar conjuntos mutuamente excluyentes. La forma correcta es comenzar a pensar en términos de dimensiones, que es mucho más consistente en todos los problemas.


Finalmente, recomendaría el libro Algorithm Design. Tiene un capítulo cada uno sobre varios paradigmas de algoritmos, como algoritmos codiciosos, programación dinámica, divide y vencerás, etc. Los ejercicios al final del capítulo son factibles en número y cubren problemas básicos y avanzados.

Aumenta tu habilidad para pensar recursivamente. La habilidad necesaria para la experiencia en programación dinámica es idéntica al pensamiento recursivo.

La programación dinámica es una recursión implementada de abajo hacia arriba. La razón por la que la programación dinámica tiene un nombre diferente a la recursión es que la programación dinámica se hace necesaria cuando la recursión normal exige múltiples (posiblemente exponencialmente muchas) llamadas del mismo subproblema. Con la programación dinámica, este problema se evita yendo de abajo hacia arriba, almacenando los resultados a medida que avanza y buscándolos (en lugar de volver a calcularlos) cuando son necesarios por subproblemas posteriores.

Los números de Fibonacci son un ejemplo típico. F (n) = F (n-1) + F (n-2) recursivamente requiere n número exponencial de llamadas recursivas. Pero, F (n) se puede calcular en n pasos calculando F (i) de abajo hacia arriba, de i = 1 a n, almacenando las respuestas a medida que avanza –

  • En primer lugar, comience a aprender algunos problemas básicos de DP, como la subsecuencia común más larga, la distancia de edición, la ruta de costo mínimo, etc. de algoritmos – GeeksforGeeks
  • Luego comience a practicar con A2 Online Judge, ya que contiene preguntas sabias de nivel de varios OJ.
  • Si se encuentra con un problema, pruébelo durante 2–3 horas y luego no lo entienda, entonces está bien leer los editoriales, ya que DP es ese tema que requiere mucha práctica y su nivel de pensamiento recursivo para una solución también. importa

¡Feliz codificación!

Espero eso ayude.

Aprenda PHP Bootstrap MySql y Ajax

y también para asegurar el futuro, también aprenda Mongo DB Nods.js Angular.js React