Une explication Java détaillée des questions classiques de la série de programmation dynamique sur Leetcode

Pendant la prévention des épidémies maison, leetcode a balayé plus de dix questions d'algorithmes de programmation dynamique linéaire, il est temps de résumer et d'affiner l'idée DP.
J'ai divisé les sujets résumés en deux types de correspondance de séquence et de vie.
Sujets d'actualité représentatifs de la première catégorie:
leetcode300. La plus longue sous-séquence ascendante
leetcode53. La sous-
séquence maximale et leetcode1143. La plus longue sous-séquence commune
leetcode72. Distance de modification

Questions brûlantes représentatives de la deuxième catégorie:
leetcode121. Le meilleur moment pour acheter et vendre des actions leetcode122. Le meilleur moment pour
acheter et vendre des actions II
leetcode322. Changer de changement
Voici les idées et la mise en œuvre du code de 7 questions

300. La sous-séquence ascendante la plus longueInsérez la description de l'image ici

  • Idée: utilisez un tableau pour stocker la séquence ascendante, la clé est que la dernière sortie est la longueur de la séquence, donc la longueur est garantie et il n'est pas nécessaire d'enregistrer le vrai sous-ordre à tout moment.
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 raison pour laquelle j'ai écrit cette question en premier est parce que l'algorithme gourmand a été utilisé auparavant, et la recherche binaire est plus difficile à penser

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. Somme maximale de sous-séquence

  • Idée: La difficulté du problème de sous-séquence est la séquence discontinue. La définition dp [i] indique le degré de la sous-séquence la plus croissante se terminant par le nombre nums [i]. dp [i] dépend du résultat de dp [i-1], et chaque récursivité doit être enregistrée avec max, et le résultat final est calculé de bas en haut. Prenons un exemple simple pour pousser: nums = [1, -2,3], puis dp [0] = nums [0] = 1;
  • étape 1: dp [1] = dp [0] + nums [1] = -1; à ce moment max = 1> dp [1], donc le courant max reste inchangé;
  • étape 2: dp [2] = dp [1] + nums [2] = 1; maintenant max <dp [2], donc max est mis à jour en dp [2]
  • De cette façon, max est enfin l'animation de l'idée d'algorithme dp [i] maximum
   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 sous-séquence commune la plus longue

Insérez la description de l'image iciInsérez la description de l'image ici

  • Idées:
    ici, je recommande un jeune frère qui montre des idées d'algorithmes sur un tableau blanc sur youtube. Ses explications vidéo sont très détaillées, ce qui peut non seulement aider à comprendre à fond, mais aussi améliorer l'anglais, le meilleur des deux mondes: [Explication détaillée de l'algorithme LeetCode] L'
    état intermédiaire de la sous- séquence commune la plus longue Dans un tableau à deux dimensions, dp [i] [j] représente la position des i premiers bits de la chaîne 1 et des j premiers bits de la chaîne 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. Modification de la distance

Le titre

  • Idée: ce type de solution au problème de programmation dynamique de deux chaînes peut utiliser la même méthode que la sous-séquence ascendante la plus longue, en utilisant deux pointeurs i et j pour pointer vers la fin des deux chaînes, puis étape par étape pour affiner le problème. Échelle.
    dp [i] [j]: nombre minimal d'étapes nécessaires pour convertir les i premiers bits de word1 en j premiers 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];
    }

Le problème de la série de stock recommande une pensée bien résumée: un moyen d'éliminer 6 problèmes de stock
Insérez la description de l'image ici

  • Idée: le profit réalisé au cours de la journée précédente est le bénéfice de la journée précédente, le premier jour est de soustraire les prix [i] du bénéfice si "acheter des stocks" et de donner des bénéfices si "vendre des stocks" Augmentez les prix [i]. Le profit maximum à ce moment est le plus grand des deux choix possibles.
    Par conséquent, il peut y avoir une sous-structure optimale:
    dp [i] [0] représente le profit détenu lorsque le stock n'est pas détenu le i-ème jour; dp [i] [0] = Math.max (dp [i-1] [0], dp [i-1] [1] + prix [i]);
    et dp [i] [1] représente le bénéfice tiré de la détention du stock le i-ème jour dp [i] [1] = Math.max (dp [i-1 ] [1], -prix [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];
    }
}

Insérez la description de l'image ici
+ Idée: L'idée est fondamentalement la même que la question précédente. La différence est que le nombre de transactions passe de 1 à illimité.
Alors dp [i] [1] est égal à dp [i-1] [1] et dp [i-1] [0 ] -prices [i] Le plus gros résultat. Puisqu'il y a également une transaction d'achat dans les i jours précédents, c'est-à-dire que le profit obtenu le i jour d'achat d'actions est directement lié à l'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. Modifier le changement

Insérez la description de l'image ici

  • Idée: L'état optimal dp [i] représente le nombre minimum de pièces nécessaires pour le montant i. Le problème à noter est que chaque nombre dp [] doit être supérieur au montant total lors de l'initialisation, ce qui est un cas extrême où les pièces sont toutes 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 cet article vous est utile, veuillez l'aimer et faites le moi savoir, je travaillerai plus dur pour partager un bon contenu avec vous, merci ~

Publié 27 articles originaux · loué 4 · visites 2178

Je suppose que tu aimes

Origine blog.csdn.net/smile001isme/article/details/105472635
conseillé
Classement