Algoritmo de programación dinámica OJ pincel preguntas

Programación DinámicaProgramación Dinámica

DP para abreviar, tiene las siguientes tres características: 1. Descomponer el problema original en varios subproblemas similares, 2. Todos los subproblemas solo necesitan ser resueltos una vez, 3. Almacenar las soluciones de los subproblemas.

A diferencia de la recursividad, la solución del problema de neutrones recursivos no se guarda, se pasa a través del marco de la pila de funciones.

Las ecuaciones de estado y de transición se refieren a la relación de recurrencia entre subproblemas y subproblemas.

Los problemas de programación dinámica generalmente se consideran desde las siguientes cuatro perspectivas: 1. Definición de estado (subproblemas abstractos) 2. Definición de ecuaciones de transición entre estados (relación de recurrencia) 3. Inicialización de estado (es decir, subproblemas iniciales) 4 Devuelve el resultado (generalmente el valor de retorno de algunos estados). El estado definido debe formar una relación recursiva.

La esencia de la programación dinámica es la definición del estado del problema y la definición de la ecuación de transición de estado (el estado y la relación recursiva entre los estados).

Escenarios aplicables: valor máximo/mínimo, si es factible, si es cierto, el número de planes.

secuencia de Fibonacci JZ10

Tema Descripción

Todos conocen la secuencia de Fibonacci, ahora se requiere ingresar un número entero positivo n, genere el elemento n-ésimo de la secuencia de Fibonacci.

ideas para resolver problemas

1. Recursividad

De acuerdo con la expresión de la función, el código se puede escribir

class Solution {
public:
    int Fibonacci(int n) {
        if(0 == n)
            return 0;
        if(n == 1 && 2 == n)
            return 1;
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
};

La complejidad del tiempo será relativamente grande, aumentando exponencialmente.

2. Solución de regla dinámica

Complejidad temporal O(N)

  • Indique F(i): el valor del elemento i;
  • Resultado devuelto: el enésimo número de Fibonacci F(n);
  • Ecuación de transición de estado: F(i) = F(i-1) + F(i-2);
  • Estado inicial: F(0) = 0, F(1) = 1

Hay un total de n+1 artículos, contando desde el artículo 0. Entonces es necesario crear una matriz de tamaño n+1 para guardar el valor del estado intermedio

//空间复杂度O(N)
class Solution {
public:
    int Fibonacci(int n) {
        //创建一个数组,用于保存中间状态的解
        int* F = new int[n + 1];
        //初始化F[0] F[1]
        F[0] = 0;//f(0)
        F[1] = 1;//f(1)
        //状态转移方程
        for(int i = 2; i <= n; i++)
        {
            F[i] = F[i-1] + F[i-2];
        }
        return F[n];
    }
};
---------------优化---------------
//空间复杂度O(1)
class Solution {
public:
    int Fibonacci(int n) {
        int fn = 0;
        int fn1 = 1;//f(n-1)--f(1)
        int fn2 = 0;//f(n-2)--f(0)

        for(int i = 2; i <= n; i++)
        {
            //f(n) = f(n-1) + f(n-2)
            fn = fn1 + fn2;
            //f(n-2) -> f(n-1) -> f(n)
            fn2 = fn1;
            fn1 = fn;
        }
        return fn;
    }
};

CC12 dividir palabras y oraciones

Tema Descripción

Dada una cadena s y un conjunto de palabras dict, determine si s se puede dividir en una secuencia de palabras con espacios de modo que todas las palabras de la secuencia sean palabras en dict (la secuencia puede contener una o más palabras).

ideas para resolver problemas

1. Solución de regla dinámica

Pregunta: si la cadena s se puede dividir con éxito --> estado abstracto

  • Indique F(i): si los primeros i caracteres se pueden dividir de acuerdo con las palabras del dictado;

  • Recurrencia de estado: j < i && F(j) && substr[j+1, i] se puede encontrar en el diccionario

    F(i): true{j < i && F(j) && substr[j+1, i]能在词典中找到} OR false; En j < i, siempre que se pueda encontrar uno F(j) == truey los caracteres de j+1 a i se puedan encontrar en el diccionario, entonces F(i) == true;

  • Valor inicial F(0) = verdadero: Para aquellos cuyo valor inicial no se puede determinar, se puede introducir un estado vacío que no representa el significado real.Como valor del estado vacío inicial del estado, es necesario asegurarse de que la recursividad del estado se puede llevar a cabo de manera correcta y sin problemas.Qué valores se pueden verificar con ejemplos simples;

  • Devuelve el resultado F (longitud de la cadena)

Por ejemplo, s = "leetcode", dict = ["leet", "code"].

F(8) : F(0) && [0, 8]
	   F(1) && [2, 8]
       F(2) && [3, 8]
       F(3) && [4, 8]
       F(4) && [5, 8] -> F(0) && [0,4] + F(1) && [1,3]+...//要求true == F(0) && [0,4], 故设F(0)==true
       F(5) && [6, 8]
       F(6) && [7, 8]
       F(7) && [8, 8]
class Solution {
public:
    bool wordBreak(string s, unordered_set<string> &dict) {
        //状态结果返回
        vector<bool> canBreak(s.size() + 1, false);
        //初始状态
        canBreak[0] = true;
        //状态转移方程
        for(int i = 1; i <= s.size(); i++)
        {
            for(int j = 0; j < i; j++)
            {
                if(canBreak[j] && (dict.find(s.substr(j, i - j)) != dict.end()))
                    canBreak[i] = true;
            }
        }
        return canBreak[s.size()];
    }
};

Triángulo CC31

Tema Descripción

Dado un triángulo, calcule la suma mínima de caminos desde la parte superior hasta la parte inferior del triángulo, con cada paso moviéndose a la siguiente fila de números adyacentes. Por ejemplo [[20],[30,40],[60,50,70],[40,10,80,30]], la suma más pequeña de caminos de arriba a abajo es 20 + 30 + 50 + 10 = 110.

ideas para resolver problemas

1. Solución de regla dinámica

Es decir, [0][0]se debe caminar, y se debe completar un camino hasta llegar a la última línea.

Encontrar el camino más corto de la fila i es encontrar el camino más corto de la fila i-1 + un número en esta fila.

  • estado:

    Subestado: el camino más corto de (0, 0) a (1, 0), (1, 1), (2, 0), ... (n, n) y

    F(i,j): el camino más corto de (0, 0) a (i, j) y

  • Recurrencia de estado:

    F(i,j) = min( F(i-1, j-1), F(i-1, j) ) + triangle[i][j]. El caso de j=0 debe procesarse, y el caso de i=j también debe procesarse (es decir, los puntos límite de j=0 e i==j tienen solo una ruta)

  • Valor inicial:

    F(0,0) = triangle[0][0]

  • resultado devuelto:

    min(F(n-1, i))

class Solution {
public:
    int minimumTotal(vector<vector<int> > &triangle) {
        if(triangle.empty()) return 0;
        //初始值
        vector<vector<int>> min_sum(triangle);//初始化F[0][0]
        //状态递推
        int line = triangle.size();
        for(int i = 1; i < line; i++)//从第1行开始走,走到最后一行才算走完
        {
            for(int j = 0; j <= i; j++)
            {
                if(j == 0) min_sum[i][j] = min_sum[i-1][j];
                else if(i == j) min_sum[i][j] = min_sum[i-1][j-1];
                else min_sum[i][j] = min(min_sum[i-1][j-1], min_sum[i-1][j]);
                //递推方程
                min_sum[i][j] = min_sum[i][j] + triangle[i][j];
            }
        }
        //返回结果 min(F(n-1, i))
        int result = min_sum[line - 1][0];
        for(int i = 1; i < line; i++)
        {
            result = min(min_sum[line-1][i], result);
        }
        return result;
    }
};

Supongo que te gusta

Origin blog.csdn.net/m0_61780496/article/details/130044676
Recomendado
Clasificación