动态规划 - 入门

什么是动态规划

根据维基百科的解释

  • 将复杂的问题拆分成若干个简单的子问题
  • 每个子问题仅仅解决一次,并保存他们的解
  • 最后推导出原问题的解

能够采用动态规划来解决的问题,通常具备两个特点

  • 最优子结构(最优化原理): 通过求解子问题的最优解,可以获得原问题的最优解
  • 无后效性
    • 某个状态一旦确定,则此后的演变过程不再受此前各状态的影响

零钱兑换

在这里插入图片描述

    /*
        给定不同面额的硬币 coins 和一个总金额 amount。
        编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
        如果没有任何一种硬币组合能组成总金额,返回 -1
     */
    public int coinChange(int[] coins, int amount) {
    
    
        int max = amount + 1;
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, max);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
    
    
            for (int j = 0; j < coins.length; j++) {
    
    
                if (coins[j] <= i) {
    
    
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }

最大连续子序列和

 /*
        给定一个长度为 n 的整数序列,求它的最大连续子序列和
     */
    public int maxSubArray (int[] nums) {
    
    
        if (nums.length == 0) return 0;
        //定义dp dp[n] 等于以nums[n] 结尾的最大连续序列和
        int[] dp = new int[nums.length];
        dp[0] = nums[0];

        //定义max 记录下最大连续序列和的值
        int max = nums[0];

        for (int i = 1; i < dp.length; i++) {
    
    
            //看看以每个字母结尾的最大连续子序列和
            if (dp[i - 1] <= 0)
                dp[i] = nums[i];
            else
                dp[i] = dp[i - 1] + nums[i];
            //记录下最大值
            max = Math.max(max, dp[i]);
        }
        return max;
    }

最长上升子序列

在这里插入图片描述

    /*
        最长上升子序列 LIS
     */
    public int lengthOfLIS (int[] nums) {
    
    
        if (nums.length == 0) return 0;
        //定义一个数组dp dp[n] 表示以nums[n]结尾的最长上升序列
        int[] dp = new int[nums.length];
        dp[0] = 1;
        //定义一个max 记录下 最长上升序列
        int max = 1;
        for (int i = 1; i < dp.length; i++) {
    
    
            //默认他自己是一个上升序列 再去前面找比他小拼凑起来
            dp[i] = 1;
            //每个元素往前面找 ,找到一个比他小的,就能组成一个以他结尾的上升序列
            for (int j = 0; j < i; j++) {
    
    
                if (nums[j] < nums[i])
                    dp[i] = Math.max(dp[j] + 1, dp[i]);
            }
            //记录下比较大的上升序列
            max = Math.max(max, dp[i]);
        }
        return max;
    }

最长公共子序列
在这里插入图片描述

    /*
        最长公共子序列
     */
    public int longestCommonSubsequence (String text1, String text2) {
    
    
        if (text1 == null || text2 == null) return 0;
        //定义dp数组,dp[i][j] 代表text1(0,i - 1) text2(0,j - 1)的最长公共子序列
        int[][] dp = new int[text1.length() + 1][text2.length() + 1];

        //确定遍历顺序,按照二维数组的顺序依次填充
        for (int i = 1; i <= text1.length(); i++) {
    
    
            for (int j = 1; j <= text2.length(); 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[text1.length()][text2.length()];
    }

这里可以采用滚动数组对内存进行优化

    /*
        最长公共子序列
     */
    public int longestCommonSubsequence (String text1, String text2) {
    
    
        if (text1 == null || text2 == null) return 0;
        //定义dp数组,dp[i][j] 代表text1(0,i - 1) text2(0,j - 1)的最长公共子序列
        int[] dp = new int[text2.length() + 1];

        //确定遍历顺序,按照二维数组的顺序依次填充
        for (int i = 1; i <= text1.length(); i++) {
    
    
            //采用快慢指针的思想 去记录dp[i - 1][j - 1] 
            int cur = 0;
            for (int j = 1; j <= text2.length(); j++) {
    
    
                int leftTop = cur;
                cur = dp[j];
                if (text1.charAt(i - 1) == text2.charAt(j - 1))
                    dp[j] = leftTop + 1;
                else
                    dp[j] = Math.max(dp[j], dp[j - 1]);
            }
        }
        return dp[text2.length()];
    }

猜你喜欢

转载自blog.csdn.net/weixin_45844836/article/details/114381603