动态规划、背包问题 6/26 116-120

1.最长递增子序列( LeetCode 300 )

方法一:动态规划
此题解还有二分 + 贪心,目前难度较大,暂时放一放

class Solution {
        public int lengthOfLIS(int[] nums) {
            int n = nums.length;
            int ans = 1;
            //dp[i] = 已i下标处结尾的最长递增子序列长度
            int[] dp = new int[n];
            dp[0] = 1;
            for (int i = 1; i < n; i++) {
                int max = 0;
                //寻找以本节点结尾可以获得的最大长度
                for (int j = 0; j < i; j++) {
                    if (nums[j] < nums[i])
                        max = Math.max(max,dp[j]);
                }
                dp[i] = max + 1;//求得了以本节点结尾的最长子序列长度
                ans = Math.max(ans,dp[i]);
            }
            return ans;
        }
    }

2.最长连续递增序列( LeetCode 674 )

class Solution {
        public int findLengthOfLCIS(int[] nums) {
            int n = nums.length;
            //dp[i] = 以第i个元素结尾的最长连续子序列长度
            int[] dp = new int[n];
            dp[0] = 1;
            int ans = 1;
            for (int i = 1; i < n; i++) {
                dp[i] = nums[i] > nums[i - 1] ? dp[i - 1] + 1 : 1;
                ans = Math.max(ans,dp[i]);
            }
            return ans;
        }
    }

3.分割等和子集( LeetCode 416 )01背包

方法一:动态规划
时间复杂度O(NM)
空间复杂度O(NM)

   //分割等和子集
class Solution {
        public boolean canPartition(int[] nums) {
            int sum = 0;
            for (int x:nums) sum += x;
            int target = sum / 2;
            if (target * 2 != sum)return false;//如果sum为奇数则不能拆分为等和子集
            //问题转化为能不能从nums中选出一些元素和为target
            //dp[i][j] = 从前i个元素中进行选取,总和不超过j的最大值
            int[][] dp = new int[nums.length][target + 1];
            //初始化第一行,只有nums[0] <= i才能装进背包
            for (int i = 0; i <= target; i++) {
                dp[0][i] = nums[0] <= i ? nums[0] : 0;
            }
            for (int i = 1; i < nums.length; i++) {
                for (int j = 0; j <= target; j++) {
                    //不选第i个元素的最大价值
                    int no = dp[i - 1][j];
                    //选第i个元素的最大价值
                    int yes = nums[i] <= j ? dp[i - 1][j - nums[i]] + nums[i] : 0;
                    dp[i][j] = Math.max(no,yes);
                }
            }
            return dp[nums.length - 1][target] == target;
        }
    }

方法二:动态规划 优化
滚动数组优化
时间复杂度O(NM)
空间复杂度O(M)

//分割等和子集
class Solution {
        public boolean canPartition(int[] nums) {
            int sum = 0;
            for (int x:nums) sum += x;
            int target = sum / 2;
            if (target * 2 != sum)return false;//如果sum为奇数则不能拆分为等和子集
            //问题转化为能不能从nums中选出一些元素和为target
            //dp[i][j] = 从前i个元素中进行选取,总和不超过j的最大值
            int[][] dp = new int[2][target + 1];
            //初始化第一行,只有nums[0] <= i才能装进背包
            for (int i = 0; i <= target; i++) {
                dp[0][i] = nums[0] <= i ? nums[0] : 0;
            }
            for (int i = 1; i < nums.length; i++) {
                for (int j = 0; j <= target; j++) {
                    //不选第i个元素的最大价值
                    //(i - 1) & 1 上一行对应的行下标
                    int no = dp[(i - 1) & 1][j];
                    //选第i个元素的最大价值
                    int yes = nums[i] <= j ? dp[(i - 1) & 1][j - nums[i]] + nums[i] : 0;
                    //i & 1本行对应的行下标
                    dp[i & 1][j] = Math.max(no,yes);
                }
            }
            return dp[(nums.length - 1) & 1][target] == target;
        }
    }

方法三:动态规划 优化
一维数组优化
时间复杂度O(NM)
空间复杂度O(M)

//分割等和子集
class Solution {
        public boolean canPartition(int[] nums) {
            int sum = 0;
            for (int x:nums) sum += x;
            int target = sum / 2;
            if (target * 2 != sum)return false;//如果sum为奇数则不能拆分为等和子集
            //问题转化为能不能从nums中选出一些元素和为target
            //dp[j] = 总和不超过j的最大值
            int[] dp = new int[target + 1];
            for (int num: nums) {//前i个元素中选取
                //逆序填充dp数组
                for (int i = target; i >= num ; i--) {
                    //选与不选第i个元素的最大值
                    dp[i] = Math.max(dp[i],dp[i - num] + num);
                }
            }
            return dp[target] == target;
        }
    }

4.最长重复子数组( LeetCode 718 )

动态规划

 class Solution {
        public int findLength(int[] nums1, int[] nums2) {
            int n = nums1.length;
            int m = nums2.length;
            int ans = 0;
            //dp[i][j] = 以nums1中下标i结尾,nums2中下标j结尾的最长公共子数组
            int[][] dp = new int[n][m];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    if (nums1[i] == nums2[j]){//相等查看前一个元素的最长子数组
                        dp[i][j] = (i - 1 >= 0 && j - 1 >= 0) ? dp[i - 1][j - 1] + 1 : 1;
                    }else {//不相等,公共子数组长度为0
                        dp[i][j] = 0;
                    }
                    ans = Math.max(ans,dp[i][j]);
                }
            }
            return ans;
        }
    }

5.最长公共子序列( LeetCode 1143 )

动态规划

 class Solution {
        public int longestCommonSubsequence(String text1, String text2) {
            int m = text1.length();
            int n = text2.length();
            /*
            dp[i][j] = text1下标i及i之前,text2下标j及j之前的最长公共子序列
            在i,j处有两种情况
            情况一:text1[i] = text2[j]
            dp[i][j] = dp[i - 1][j - 1] + 1;
            情况二:text1[i] != text2[j]
            dp[i][j] = Math.max(dp[i - 1][j],dp[j - 1][i]);
             */
            int[][] dp = new int[m][n];
            int ans = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                 int x = j - 1 >= 0 ? dp[i][j - 1] : 0;//dp[i,j - 1]
                 int y = i - 1 >= 0 ? dp[i - 1][j] : 0;//dp[i - 1 ,j]
                 int z = (j - 1 >= 0 && i - 1 >= 0) ? dp[i - 1][j - 1] : 0;//dp[i - 1][j - 1]
                 if (text1.charAt(i) == text2.charAt(j)){
                     dp[i][j] = 1 + z;
                 }else {
                     dp[i][j] = Math.max(x,y);
                 }
                 ans = Math.max(ans,dp[i][j]);
                }
            }
            return ans;
        }
    }

你可能感兴趣的:(动态规划,算法,leetcode)