[Likou] La "mejor" serie de preguntas de programación dinámica

1. Pasos para resolver problemas de programación dinámica.

La idea general para resolver problemas de Programación Dinámica (DP) se puede dividir en los siguientes pasos:

  1. Defina el estado : primero, debe definir claramente el estado del problema. El estado es información clave que describe el problema y generalmente incluye las variables que deben optimizarse. La definición del estado debe reflejar las características del problema y puede calcularse de forma recursiva o iterativa.
  2. Definir la ecuación de transición de estados : una vez definido el estado, el siguiente paso es establecer la relación entre estados, que es la ecuación de transición de estados. Las ecuaciones de transición de estado describen cómo calcular un nuevo estado a partir de un estado conocido. Este es el núcleo del problema de la programación dinámica.
  3. Inicialización : determina el valor del estado inicial, generalmente las condiciones de contorno o el caso base del problema. Estos estados iniciales son el punto de partida del algoritmo de programación dinámica.
  4. Cálculo recursivo o iterativo : utilice ecuaciones de transición de estado para calcular gradualmente la solución óptima al problema a partir del estado inicial. Esto se puede lograr mediante un enfoque iterativo ascendente o un enfoque recursivo de arriba hacia abajo.
  5. Registrar ruta (opcional): si necesita restaurar la ruta específica de la solución óptima, puede registrar la fuente de cada estado durante el proceso de transferencia de estado para facilitar la restauración posterior de la ruta.
  6. Resultados devueltos : De acuerdo con los requisitos del problema, obtenga la solución óptima requerida o el valor óptimo del estado final.
  7. Optimización de la complejidad del espacio (opcional): en algunos casos, la complejidad del espacio de los algoritmos de programación dinámica se puede optimizar, como retener solo los estados intermedios necesarios en lugar de registrarlos todos.

2. Centrarse en ejemplos clásicos

5. La subcadena palíndromo más larga.

Dada una cadena s, encuentre la subcadena palíndromo más larga en s.
Si el orden inverso de una cuerda es el mismo que el de la cuerda original, la cuerda se llama cuerda palíndromo.

Ejemplo 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

Ejemplo 2:

输入:s = "cbbd"
输出:"bb"
  • Ideas
  1. Definir estado: defina el estado dp[i][j]para indicar s[i...j]si la cadena es una subcadena palíndromo
  2. Definir la ecuación de transición de estado
  • Si s[i]no s[j]es el mismo que el carácter, obviamente no es s[i...j]una cadena palíndromo.
  • si es igual s[i]al s[j]personaje
    • Si iy json subíndices adyacentes, es decir, j-i==1entonces , s[i...j]obviamente es una cadena palíndromo.
    • Si iy jno son subíndices adyacentes, si s[i...j]es un palíndromo depende de s[i+1...j-1]si es un palíndromo.

Entonces la ecuación de transición de estado:

	if (s[i] == s[j]) {
    
    
        dp[i][j] = j - i == 1 ? true : dp[i + 1][j - 1];
     } else {
    
    
        dp[i][j] = false;
     }
  1. Inicialización: cada carácter es una cadena palíndromo, por lo que dp[i][i]se inicializa en true (0<=i<n), y otras inicializaciones sonfalse
  2. Cálculo recursivo o iterativo: como dp[i][j]depende dp[i+1][j-1], el estado se transfiere de una cadena más corta a una cadena más larga
  3. Resultado de retorno: debido a que necesitamos encontrar la cadena de palíndromo más larga, definimos una startvariable para representar el subíndice inicial de la subcadena de palíndromo más larga y una maxLenvariable para representar la longitud de la subcadena de palíndromo más larga, y luego cada vez que encontramos una subcadena de palíndromo , determine si la longitud de la cadena es mayor que la longitud de la subcadena palíndromo más larga actual y, si es más larga, actualice la información de las dos variables marcadas con la subcadena palíndromo más larga. El retorno final s.substr(start, maxLen)es la subcadena palíndromo más larga.
  • Código C++
class Solution {
    
    
public:
    string longestPalindrome(string s) {
    
    
        if (s.size() <= 1) {
    
    
            return s;
        }
        int n = s.size();
        // 定义状态:dp[i][j]表示s[i...j]是否为回文串
        vector<vector<bool>> dp(n, vector<bool>(n, false));
        // 初始化
        for (int i = 0; i < n; ++i) {
    
    
            dp[i][i] = true;
        }
        int start = 0;  // 记录最长回文子串的起始下标
        int maxLen = 1; // 记录最长回文子串的长度(最少一个字符本身就是回文串)
        for (int len = 2; len <= n; ++len) {
    
      // 注意需要从长度较短的字符串向长度较长的字符串进行转移
            for (int i = 0; i < n; ++i) {
    
    
                int j = len + i - 1;
                if (j >= n) {
    
    
                    break;
                }
                // 状态转移方程
                if (s[i] == s[j]) {
    
    
                    dp[i][j] = j - i == 1 ? true : dp[i + 1][j - 1];
                } else {
    
    
                    dp[i][j] = false;
                }
                
                // 更新最长回文子串信息
                if (dp[i][j] && j - i + 1 > maxLen) {
    
    
                    start = i;
                    maxLen = j - i + 1;
                }
            }
        }
        return s.substr(start, maxLen);
    }
};

32. Soporte válido más largo

Dada una cadena que contiene solo '(' y ')', encuentre la longitud de la subcadena de corchetes válida más larga (bien formada y consecutiva).

Ejemplo 1:

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

Ejemplo 2:

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

Ejemplo 3:

输入:s = ""
输出:0

pista:

0 <= s.length <= 3 * 104
s[i] 为 '(' 或 ')'
  • Ideas
  1. Definir estado: utilizar dp[i]representantesiLa longitud de corchete válida más larga que termina en carácter(Empiezo de 0 a n-1), por ejemplo ((), hay dp[0]=0,dp[1]=0,dp[2]=2;
  2. Defina la ecuación de transición de estado:
  • Si s[i]es un corchete izquierdo (, obviamente dp[i]=0, porque el corchete izquierdo no puede ser el carácter final de una cadena de corchetes válidos [como una cadena ((]
  • Si s[i]es un corchete derecho ), es necesario discutirlo caso por caso dependiendo de si el carácter anterior es un corchete izquierdo o un corchete derecho.
    • Si s[i-1]es un corchete izquierdo (, entonces dp[i]=dp[i-2]+2(i>=2), [como una cadena (())(), dp[5]=dp[3]+2=4+2=6]
    • Si s[i-1]es un corchete derecho ), primero busque i-1el carácter antes del corchete válido más largo que termine con el carácter enésimo, es decir s[i-dp[i-1]-1],
      • Si este carácter es un corchete izquierdo (, entonces dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2], [por ejemplo, cadena ()(()), dp[5]=dp[4]+2+dp[1]=2+2+2=6]
      • Si este carácter es un corchete derecho ), entonces dp[i]=0(no puede formar una cadena de corchetes válida) [por ejemplo, )())es una cadena no válida]

La ecuación de transición de estado se abrevia como:

if (s[i] == ')') {
    
    
    if (s[i - 1] == '(') {
    
    
        dp[i] = i >= 2 ? dp[i - 2] + 2 : 2;
    } else if (s[i - 1] == ')' && i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] == '(') {
    
    
        dp[i] =  i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] + dp[i - 1] + 2 : dp[i - 1] + 2;
    }
}
  1. inicialización:dp[i]=0
  2. Cálculo recursivo o iterativo: comience a recorrer desde iigual a , debido a que al menos dos caracteres pueden formar un paréntesis válido, debe ser1dp[0]0
  3. Resultados de devolución: cada dp[i]proceso de cálculo se actualiza al mismo tiempomaxLen
  • Código C++
class Solution {
    
    
public:
    int longestValidParentheses(string s) {
    
    
        int n = s.size();
        vector<int> dp(n, 0);
        int maxLen = 0;
        for (int i = 1; i < n; ++i) {
    
    
            if (s[i] == '(') {
    
    
                continue;
            }
            if (s[i - 1] == '(') {
    
    
                dp[i] = i >= 2 ? dp[i - 2] + 2 : 2;
            } else if (s[i - 1] == ')' && i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] == '(') {
    
    
                dp[i] =  i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] + dp[i - 1] + 2 : dp[i - 1] + 2;
            }
            maxLen = max(maxLen, dp[i]);
        }
        return maxLen;
    }
};

53. Suma máxima de subarreglo

Dada una matriz de números enteros, busque una submatriz continua con la suma máxima (la submatriz contiene al menos un elemento) y devuelva su suma máxima.

Un subarreglo es una porción contigua de un arreglo.

Ejemplo 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

Ejemplo 2:

输入:nums = [1]
输出:1

Ejemplo 3:

输入:nums = [5,4,-1,7,8]
输出:23

pista:

1 <= nums.length <= 105
-104 <= nums[i] <= 104
  • Ideas
  1. Definir estado: dp[i]representado porSuma máxima de una matriz que termina con el número i-ésimo
  2. Defina la ecuación de transición de estado:
    dp[i] = max(dp[i - 1] + nums[i], nums[i]); (i>=1)
    
    Si la suma máxima del enésimo inúmero más la matriz que termina en el enésimo número es menor que el enésimo número en sí, es mejor no sumarlo.i-1i
  3. inicialización:dp[0]=nums[0]
  4. Cálculo recursivo o iterativo: i=1recorrer de principio a finn-1
  5. Resultado de retorno: establezca un maxSumvalor inicial de y el tamaño se nums[0]actualizará en cada ronda de recorrido posterior.maxSum
  • Código C++
    • versión de matriz dp:
      class Solution {
              
              
      public:
          int maxSubArray(vector<int>& nums) {
              
              
              int n = nums.size();
              // dp[i]表示以第i个数结尾的数组的最大连续子数组的和
              vector<int> dp(n, 0);
              // 初始化
              dp[0] = nums[0];
              int maxSum = nums[0];
              for (int i = 1; i < n; ++i) {
              
              
                  dp[i] = max(dp[i - 1] + nums[i], nums[i]);
                  maxSum = max(maxSum, dp[i]);
              }
              return maxSum;
      }
      
    • Versión simplificada: dp[i]solo dp[i-1]se utilizan los datos, por lo que puedes usar directamente una variable sumpara reemplazarlos.
      class Solution {
              
              
      public:
          int maxSubArray(vector<int>& nums) {
              
              
              int n = nums.size();
              int sum = nums[0];
              int maxSum = sum;
              for (int i = 1; i < n; ++i) {
              
              
                  sum = max(sum + nums[i], nums[i]);
                  maxSum = max(maxSum, sum);
              }
              return maxSum;
          }
      };
      

Supongo que te gusta

Origin blog.csdn.net/weixin_36313227/article/details/133396298
Recomendado
Clasificación