Una explicación detallada de Java de preguntas clásicas en la serie de programación dinámica en Leetcode

Durante la prevención de epidemias en el hogar, leetcode analizó más de diez preguntas de algoritmos de programación dinámica lineal, es hora de resumir y refinar la idea DP.
Dividí los temas resumidos en dos tipos de secuencia de coincidencias y vida.
Temas candentes representativos de la primera categoría:
leetcode300. La subsecuencia ascendente más larga
leetcode53. La subsecuencia máxima y
leetcode1143. La subsecuencia común más larga
leetcode72. Distancia de edición

Preguntas candentes representativas de la segunda categoría:
leetcode121. El mejor momento para comprar y vender acciones leetcode122. El mejor momento para
comprar y vender acciones II
leetcode322. Cambiar el cambio Las
siguientes son las ideas y la implementación del código de 7 preguntas

300. La subsecuencia ascendente más largaInserte la descripción de la imagen aquí

  • Idea: Use una matriz para almacenar la secuencia ascendente, la clave es que la última salida es la longitud de la secuencia, por lo que la longitud está garantizada sin la necesidad de registrar el suborden real en todo momento.
class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0) return 0;
        for(int i=1; i<nums.length; i++){
            dp[1] = 1;
            for(int j=i-1; j>1; j--){
                 if(dp[j] < dp[i]) dp[j] = dp[j-1]+1; 
            }
        }
    }
}

La razón por la que escribí esta pregunta primero es porque el algoritmo codicioso se usó antes, y la búsqueda binaria es más difícil de imaginar.

public static int lengthOfLIS(int[]nums) {
        if(nums.length < 2) return nums.length;
        //LIS数组存放最长子序列的数组,但并非时刻都存放的是最长子序列
        int[] LIS = new int[nums.length]; 
        LIS[0] = nums[0];//数组有负整数的情况
        int end = 0;
        for(int i=1; i<nums.length; i++){
            //如果该位元素大于子序列最后一位,上升子序列变长1
            if(nums[i]>LIS[end]){
                end++;   LIS[end]=nums[i];
            }
 //如果当前nums[i]小于子序列最后一位,则用二分法搜索子序列中比nums[i]大的最小数
            else{
                int left = 0,right =end;
                while(left<right){
                    int pivot = left+(right-left)/2;
                    if( LIS[pivot]< nums[i]){
                        left = pivot+1;
                    }
                    else{
                        assert LIS[pivot] >= nums[i];
                        right = pivot;
                    }
                }
                LIS[right]=nums[i];
            }
        }
        return end+1;
    }

LeetCode53. Suma de subsecuencia máxima

  • Idea: La dificultad del problema de subsecuencia es la secuencia discontinua. La definición dp [i] indica el grado de la subsecuencia más creciente que termina en el número nums [i]. dp [i] depende del resultado de dp [i-1], y cada recursión debe registrarse con max, y el resultado final se calcula de abajo hacia arriba. Usemos un ejemplo simple para empujar: nums = [1, -2,3], luego dp [0] = nums [0] = 1;
  • paso1: dp [1] = dp [0] + nums [1] = -1; en este momento max = 1> dp [1], por lo que el máximo actual permanece sin cambios;
  • paso2: dp [2] = dp [1] + nums [2] = 1; ahora max <dp [2], entonces max se actualiza a dp [2]
  • De esta manera, max es finalmente la animación de la idea máxima del algoritmo dp [i]
   public int maxSubArray(int[] nums) {
		int[] dp = new int[nums.length];
		dp[0] = nums[0];   
		int max = nums[0];
		for (int i = 1; i < nums.length; i++) {
		//nums[i] > 0,说明对结果有增益,dp[i]再加当前遍历值
		//nums[i] <= 0,说明对结果无增益,dp[i]直接更新为当前遍历数字
			dp[i] = Math.max(dp[i- 1] + nums[i], nums[i]);	
			if (max < dp[i]) {      //关键步:取每次遍历的当前最大和
				max = dp[i];
			}
		}
		return max;
   }

LeetCode1143. La subsecuencia común más larga

Inserte la descripción de la imagen aquíInserte la descripción de la imagen aquí

  • Ideas:
    Aquí recomiendo a un hermano menor que demuestre ideas de algoritmos en una pizarra en YouTube. Sus explicaciones en video son muy detalladas, lo que no solo puede ayudar a comprender a fondo, sino también a mejorar el inglés, lo mejor de ambos mundos: [Explicación detallada del algoritmo LeetCode] El
    estado intermedio de la subsecuencia común más larga es En una matriz bidimensional, dp [i] [j] representa la posición de los primeros i bits de la cadena 1 y los primeros j bits de la cadena 2.
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int n1 = text1.length(); int n2 = text2.length();
        int[][] dp = new int[n1+1][n2+1];
        for(int i=0; i<n1; i++) { dp[i][0] = 0; }
        for(int j =0; j<n2; j++){ dp[0][j] = 0; }
        for(int i=1; i<=n1; i++){
            for(int j =1; j<=n2; j++){
                if(text1.charAt(i-1)==text2.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1] + 1;
                else{
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
        return dp[n1][n2];
    }
}

LeetCode72. Distancia de edición

Titulo

  • Idea: Este tipo de solución al problema de programación dinámica de dos cadenas puede usar el mismo método que la subsecuencia ascendente más larga, usando dos punteros i y j para señalar el final de las dos cadenas, y luego paso a paso para reducir el problema. Escala
    dp [i] [j]: el número mínimo de pasos necesarios para convertir los primeros i bits de word1 en los primeros j bits de word2 .
public int minDistance(String word1, String word2) {
        int n1 = word1.length(); int n2 = word2.length();
        int[][] dp = new int[n1+1][n2+1];
        for(int i=1; i<=n1; i++) dp[i][0] = dp[i-1][0] +1;   
        for(int j=1; j<=n2; j++) dp[0][j] = dp[0][j-1] +1;  
        for(int i=1; i<=n1; i++){
            for(int j=1; j<=n2; j++){
            //word1和word2的该位字符相同,不需要改动。
                if(word1.charAt(i-1)==word2.charAt(j-1))
                   dp[i][j] = dp[i-1][j-1];
            //如果字符不同,则取该步之前的状态基础上做删除,修改,插入中的最小改动
                else 
                   dp[i][j] = Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
            }
        }
        return dp[n1][n2];
    }

El problema de la serie de existencias recomienda un pensamiento bien resumido: una forma de eliminar 6 problemas de existencias
Inserte la descripción de la imagen aquí

  • Idea: La ganancia obtenida en el i-day anterior es la ganancia en el i-day anterior; el i-th día es restar los precios [i] de la ganancia si "compra acciones", y dar ganancias si "vende acciones" Aumentar los precios [i]. El beneficio máximo en este momento es la mayor de las dos opciones posibles.
    Por lo tanto, puede haber una subestructura óptima:
    dp [i] [0] representa la ganancia que se posee cuando la acción no se mantiene el día i; dp [i] [0] = Math.max (dp [i-1] [0], dp [i-1] [1] + precios [i]);
    y dp [i] [1] representa el beneficio de mantener la acción en el día i dp [i] [1] = Math.max (dp [i-1 ] [1], -precios [i]);
class Solution {
   public int maxProfit(int[] prices) {
        int n = prices.length;
        if(prices.length==0)return 0;
        int[][] dp = new int[n][2]; //行表示第 i天,列表示是否买入当天股票
        dp[0][0] = 0; //i = 0 时 dp[i-1] 是不合法的。
        dp[0][1] = -prices[0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], -prices[i]);
        }
        return dp[n - 1][0];
    }
}

Inserte la descripción de la imagen aquí
+ Idea: La idea es básicamente la misma que la pregunta anterior. La diferencia es que el número de transacciones cambia de 1 a ilimitado.
Entonces dp [i] [1] es igual a dp [i-1] [1] y dp [i-1] [0 ] -precios [i] El resultado más grande. Debido a que también hay una transacción de compra en los días anteriores, es decir, la ganancia obtenida el día de la compra de acciones está directamente relacionada con el i-1.

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length==0) return 0;
        int[][] dp = new int[prices.length][2];
        dp[0][0] = 0; 
        dp[0][1] = -prices[0];
        for(int i=1; i<prices.length;i++){
              dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
              dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
        } 
        return dp[prices.length-1][0];
    }
}

leetcode322. Cambiar cambio

Inserte la descripción de la imagen aquí

  • Idea: El estado óptimo dp [i] representa el número mínimo de monedas requerido para la cantidad i. El problema a tener en cuenta es que cada número dp [] debe ser mayor que la cantidad total de dinero durante la inicialización, que es un caso extremo donde las monedas son todas 1.
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
      // 注意:因为要比较的是最小值,不可能是结果的初始化值就得赋一个最大值
        Arrays.fill(dp, amount + 1);
        dp[0] =0;
        for(int i=1; i<=amount; i++){
            for(int coin: coins){
        //如果可包含coin,那么剩余钱是i−coins,要兑换的硬币数是 dp[i−coins]+1
                if(coin <= i)
                   dp[i] = Math.min(dp[i],dp[i-coin]+1);
            }
        }
        return dp[amount]<=amount ? dp[amount] : -1;
    }
}

Si este artículo te resulta útil, dale me gusta y avísame, trabajaré más duro para compartir un buen contenido contigo, gracias ~

27 artículos originales publicados · elogiados 4 · visitas 2178

Supongo que te gusta

Origin blog.csdn.net/smile001isme/article/details/105472635
Recomendado
Clasificación