Programación dinámica (DP)

Idea principal

Al resolver un problema difícil, el problema se descompone en subproblemas discretos, resolviendo primero los subproblemas y luego resolviendo gradualmente el gran problema.

Similitudes y diferencias con el algoritmo divide y vencerás

  • Similitudes: La idea básica de ambos es descomponer el problema a resolver en varios subproblemas, primero resolver los subproblemas y luego obtener la solución del problema original a partir de las soluciones de estos subproblemas.
  • Diferencia: los subproblemas descompuestos por algoritmos de divide y vencerás a menudo son independientes y no están relacionados entre sí, como la clasificación rápida. Los subproblemas obtenidos mediante la descomposición dinámica de la programación a menudo no son independientes entre sí, es decir, el siguiente subproblema a menudo se resuelve sobre la base del subproblema anterior.

Problema clásico 1-01 problema de mochila

Si es un ladrón y lleva una mochila que puede contener 4 libras, hay tres tipos de bienes que puede robar:
1) Audio ($ 3,000, 4 libras)
2) Computadora portátil ($ 2,000, 3 libras)
3) Guitarra ($ 1,500, 1 libra)
Para que los bienes robados sean más valiosos, ¿qué bienes debería elegir? ¿Cuál es el valor?

1. La forma sencilla

Pruebe varias combinaciones de productos posibles y encuentre la combinación con el valor más alto. 3 elementos de 2 3 2 ^ 32Tres tipos de métodos de combinación, se puede ver que cuando los tipos de bienes aumentan, este método es muy lento y la complejidad del tiempo esO (2 n) O (2 ^ n)O ( 2n )

2. Algoritmo codicioso

Para cada selección, elija el producto con el precio unitario más alto que cumpla con las condiciones. Podemos encontrar una solución aproximada con menos gastos generales. En este problema, el resultado final de usar el algoritmo codicioso es "sólido", pero la solución aproximada no es necesariamente la solución óptima. El valor más alto del producto robado real es "laptop + guitarra".

3. Programación dinámica

Al resolver el problema de una mochila con capacidad de 4 libras, primero podemos resolver el problema de una mochila con capacidad de 3 libras, 2 libras ..., 1 libra ... Simplifique el problema de elegir el valor más alto de 3 cosas en 2 cosas ..., 1 cosa ... Al resolver los subproblemas, el gran problema original se resuelve paso a paso.

Cada algoritmo de programación dinámica comienza con una cuadrícula, y la cuadrícula del problema de la mochila es la siguiente.
Inserte la descripción de la imagen aquí
Solo necesitamos llenar la tabla fila por fila (o columna por columna de izquierda a derecha) de arriba a abajo. Cuando la tabla está llena, obtenemos la respuesta a la pregunta.

  • Tienda de guitarras: Porque lo único que se puede robar es la guitarra, y el peso de la guitarra es de 1 libra, por lo que la tienda de guitarras debería agregar "guitarra, $ 1,500".
  • Línea de audio: el peso del audio es de 4 libras, por lo que las primeras 3 cuadrículas solo pueden robar la guitarra, porque el valor del audio es más alto que el de la guitarra, por lo que la cuarta cuadrícula agrega "audio, $ 3,000".
  • Computadora portátil: debido a que la computadora portátil pesa 3 libras, las dos primeras cuadrículas siguen siendo las mismas y siguen siendo guitarras. La tercera cuadrícula se debe a que el precio de las computadoras portátiles es más alto que el de las guitarras, así que agregue una computadora portátil. La cuarta cuadrícula, porque computadoras portátiles y las guitarras son más caras que los parlantes, por lo que debe agregar "laptops, guitarras, $ 3,500", que es la respuesta a esta pregunta.

La cuadrícula final debería verse como la imagen de abajo.
Inserte la descripción de la imagen aquí
Mediante el análisis anterior, podemos calcular el valor de cada cuadrícula mediante la siguiente fórmula.
Inserte la descripción de la imagen aquí
El código de implementación es el siguiente:

public class KnapsackProblem {
    
    

    public static void knapsackProblem(int[] weight, int[] value, int capacity) {
    
    

        //记录不同容量不同商品数量的总价值
        //为了方便理解和编写,我们从数组的第一行第一列开始统计
        int[][] totalValue = new int[weight.length + 1][capacity + 1];
        //记录对应位置应该装的商品id
        String[][] goods = new String[weight.length + 1][capacity + 1];

        //初始化:避免null, 与算法无关
        for (int i = 0; i < totalValue.length; i++) {
    
    
            goods[i][0] = "";
        }
        for (int j = 0; j < totalValue[0].length; j++) {
    
    
            goods[0][j] = "";
        }

        for (int i = 1; i < totalValue.length; i++) {
    
    
            for (int j = 1; j < totalValue[i].length; j++) {
    
    
                //判断每个网格放入的内容
                if (weight[i - 1] <= j) {
    
    
                    int temp = value[i - 1] + totalValue[i - 1][j - weight[i - 1]];
                    if (temp > totalValue[i - 1][j]) {
    
    
                        totalValue[i][j] = temp;
                        goods[i][j] = (i - 1) + " " + goods[i - 1][j - weight[i - 1]];
                    } else {
    
    
                        totalValue[i][j] = totalValue[i - 1][j];
                        goods[i][j] = goods[i - 1][j];
                    }

                } else {
    
    
                    totalValue[i][j] = totalValue[i - 1][j];
                    goods[i][j] = goods[i - 1][j];
                }
            }
        }

        System.out.println("应该偷的商品序号:" + goods[weight.length][capacity]);
        System.out.println("商品总价值:" + totalValue[weight.length][capacity]);

    }

    public static void main(String[] args) {
    
    
        int[] weight = new int[]{
    
    1, 4, 3};
        int[] value = new int[]{
    
    1500, 3000, 2000};
        int capacity = 4;
        knapsackProblem(weight, value, capacity);

    }

}

¿Cómo juzgar si este problema puede resolverse mediante programación dinámica?

① Cuando es necesario optimizar un índice determinado bajo determinadas restricciones, se puede utilizar la programación dinámica.
② El problema se puede descomponer en subproblemas y se puede promover la solución de subproblemas para resolver el problema.

¿Cómo utilizar la programación dinámica para resolver el problema?

① Cada solución de programación dinámica involucra una cuadrícula.Cada celda en la cuadrícula representa un subproblema del problema, así que lo más importante es ¿cómo dividir el problema en subproblemas? ¿Cuál es el eje de coordenadas correspondiente a la cuadrícula?
② ¿Qué necesito agregar al valor en la celda? El valor de la celda suele ser el valor que desea optimizar. Por ejemplo, en el problema de la mochila, el valor de la celda es el valor de la mercancía.

Problema clásico 2: la subsecuencia común más larga

Dadas dos cadenas, encuentre su subsecuencia común más larga. Haga clic aquí para obtener preguntas detalladas .

Podemos descomponer este problema para resolver la subsecuencia común más larga de estas dos subcadenas de cadena.
Tales como: buscando "fosh"y "fish"la subsecuencia común más larga, podemos buscar la primera "f"y "f"la subsecuencia común más larga, luego buscar "fo"y "f"la subsecuencia común más larga, y así, la siguiente pregunta es la solución de un problema sobre la base de Resuelva lo anterior nuevamente, por lo que la cuadrícula es como se muestra en la siguiente figura: Después de
Inserte la descripción de la imagen aquí
obtener la cuadrícula, tratamos de agregar gradualmente datos a la cuadrícula para derivar la fórmula de cálculo para cada cuadrícula. El resultado final de la cuadrícula es el siguiente.
Inserte la descripción de la imagen aquí
Analizando la fórmula de cálculo de cada cuadrícula de forma sincrónica al agregar datos, podemos obtener la siguiente fórmula.
Inserte la descripción de la imagen aquí
el código se muestra a continuación:

public class LongestCommonSubsequence {
    
    
    public int longestCommonSubsequence(String text1, String text2) {
    
    
        //为了方便计算,网格分别再第一行和第一列前插入空行和空列
        int[][] grid = new int[text1.length() + 1][text2.length() + 1];
        //因此,此处从1开始
        for (int i = 1; i < grid.length; i++) {
    
    
            for (int j = 1; j < grid[i].length; j++) {
    
    
                //此处访问text中的值要减1
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
    
    
                    grid[i][j] = grid[i-1][j-1] + 1;
                } else {
    
    
                    grid[i][j] = Math.max(grid[i - 1][j], grid[i][j - 1]);
                }
            }
        }
        //返回最终结果
        return grid[text1.length()][text2.length()];
    }

    //测试代码
    public static void main(String[] args) {
    
    
        System.out.println(new LongestCommonSubsequence().longestCommonSubsequence("fosh", "fish"));
    }
}

Referencia: "Diagrama de algoritmo" Capítulo 9 Programación dinámica

Supongo que te gusta

Origin blog.csdn.net/AmorFati1996/article/details/111195130
Recomendado
Clasificación