zoukankan      html  css  js  c++  java
  • leetcode动态规划笔记三---单序列型

    单序列型DP

    相比一维DP,这种类型状态转移与过去每个阶段的状态都有关。
    

    Perfect Squares :

    方法一 : 自底向上 递推 + memo
    f(n) = min{f(i) + f(n-i), i = 1...n-1}

    class Solution {
        public int numSquares(int n) {
            int[] memo = new int[n];
            
            for(int i = 1; i <= n; i++){
                if(i * i <= n){
                    memo[i*i - 1] = 1;
                }
            }
            
            if(memo[n-1] == 1) return 1;
            
            memo[1] = 2;   
            for(int i = 3; i <= n; i++){
                if(memo[i-1] == 1) continue;
                
                int left = 1; 
                int right = i - 1;
                
                int mini = Integer.MAX_VALUE;
                while(left <=  right){
                    if(left + right == i){
                        mini = Math.min(mini, memo[left - 1] + memo[right - 1]);
                    }
                    left ++; right--;
                }
                memo[i-1] = mini;
            }
            
            return memo[n - 1];
        }
    }
    

    方法二: 递推 + memo
    另外,本题有个四平方数之和定理,具体参考

    class Solution {
        public int numSquares(int n) {
            int[] memo = new int[n + 1];
            memo[0] = 0;
            
            for(int i = 1; i <= n; i++){
                int mini = Integer.MAX_VALUE;
                
                for(int j = 1; j <= i; j ++){
                    int t = j*j;
                    
                    if(t > i) break;
                    if(t == i) mini = 1;
                    else mini = Math.min(mini, memo[t] + memo[i-t]);
                }
                
                memo[i] = mini;
            }
            return memo[n];
        }
    }
    

    Longest Increasing Subsequence

    方法一 : 自底向上,递推 + memo
    这道题的状态转移想了比较久,没有想出来,给出grandyang的参考.
    参考中,还介绍了nlog(n)的解法,用了二搜法。

    class Solution {
        public int lengthOfLIS(int[] nums) {
            if(nums.length == 0) return 0;
            
            int[] memo = new int[nums.length];
            memo[0] = 1;
    
            int maxi = 1;
            for(int i = 1; i < nums.length; i++){
                
                memo[i] = 1;
                
                for(int j = i - 1; j >= 0; j--){
                    if(nums[i] > nums[j])
                    memo[i] = Math.max(memo[i], memo[j] + 1);
                }
                maxi = Math.max(memo[i], maxi);
            }
            
            return maxi;
        }
    }
    

    Increasing Triplet Subsequence : 求是否存在

    方法一 : 自底向上 递推 + memo

    class Solution {
        public boolean increasingTriplet(int[] nums) {
            if(nums.length == 0) return false;
            
            int[] memo = new int[nums.length];
            memo[0] = 1;
            
            boolean exist = false;
            for(int i = 1; i < nums.length; i ++){
                memo[i] = 1;
                for(int j = 0; j < i; j ++){
                    if(nums[i] > nums[j]){
                        memo[i] = Math.max(memo[j] + 1, memo[i]);
                    }
                }
                
                if(memo[i] == 3){
                    exist = true;break;
                }
            }
            
            return exist;
        }
    }
    

    Coin Change

    方法一: 自底向上
    递推 + memo
    如果直接用用模除,将得到错误结果
    dp方法没有可优化了,用 递归 + 减枝还可以优化,参考
    减枝是一种优化方法,一般用于dfs/bfs中,通过条件过滤掉对搜索空间树的搜索,参考

    class Solution {
        public int coinChange(int[] coins, int amount) {
            
            Arrays.sort(coins);
            int[] memo = new int[amount + 1];
            
            
            for(int i = 1; i <= amount; i++){
                
                memo[i] = Integer.MAX_VALUE;
                
                for(int j = coins.length - 1; j >= 0; j--){
                    if(i < coins[j]) continue;
                    
                    if(i == coins[j]){
                        memo[i] = 1;
                        continue;
                    }
                    
                    int m = i - coins[j];//此处若修改为 m = i % coins[j]会出错
                    if(memo[m] != -1){
                        memo[i] = Math.min(memo[i], 1 + memo[m]);
                    }
                }
                
                memo[i] = (memo[i] == Integer.MAX_VALUE) ? -1 : memo[i];
            }
            
            return memo[amount];
        }
    }
    

    Integer Break

    方法一 : 自底向上, 递推 + memo
    与coin change很像

    class Solution {
        public int integerBreak(int n) {
            int[] memo = new int[n + 1];
            memo[0] = 1;
            memo[1] = 1;
            memo[2] = 1;
            
            for(int i = 3; i <= n; i++){
                int a = i / 2 + 1;
                for(int j = 1; j < a; j++){
                    int x = Math.max(j, memo[j]);
                    int y = Math.max(i - j, memo[i - j]);
                    memo[i] = Math.max(memo[i], x * y);
                }
            }
            
            return memo[n];
        }
    }
    

    Largest Divisible Subset

    方法一 : 自底向上, 递推 + memo

    class Solution {
        public List<Integer> largestDivisibleSubset(int[] nums) {
            if(nums.length < 1) return new ArrayList<>();
            
            List<List<Integer>> memo = new ArrayList<List<Integer>>();
            Arrays.sort(nums);
            
            int gIdx = 0;
            int gLen = 0;
            
            List<Integer> li = new ArrayList<>();
            li.add(nums[0]);
            memo.add(li); // 1
            
            for(int i = 1; i < nums.length; i ++){
                int longIdx = i;
                int longLen = 0;
                
                for(int j = 0; j < i; j++){
                    int tlen = memo.get(j).size();
                    if(nums[i] % memo.get(j).get(tlen - 1) == 0){
                        if(longLen < tlen){
                            longLen = tlen; longIdx = j;
                        }
                    }
                }
                
                if(longIdx == i){
                    List<Integer> li2 = new ArrayList<>();
                    li2.add(nums[i]);
                    memo.add(li2);
                }
                else{
                    List<Integer> li3 = new ArrayList<>(memo.get(longIdx));
                    li3.add(nums[i]);
                    memo.add(li3);
                    if(memo.get(i).size() > gLen){
                        gIdx = i; gLen = memo.get(i).size();
                    }                
                }
            }
            
            if(gLen == 0) return memo.get(0);
            return memo.get(gIdx);
        }
    }
    

    Combination Sum IV

    方法一: 自顶向下 回溯法 TimeLimitExceed, 11 / 17

    class Solution {
        public int combinationSum4(int[] nums, int target) {
            Arrays.sort(nums);
            return dfs(nums, target);
        }
        
        int dfs(int[] nums, int tgt){
            
            if(tgt == 0){
                return 1;
            }
            else if(tgt < 0){
                return 0;
            }
            
            int sumC = 0;
            for(int i = 0; i < nums.length; i ++){
                int newTgt = tgt - nums[i];
                if(newTgt >= 0){
                    sumC += dfs(nums, newTgt);
                }
                else{
                    break;
                }
            }
            
            return sumC;
        }
    }
    

    方法二:自底向上 递推 + memo

    class Solution {
        public int combinationSum4(int[] nums, int target) {
            Arrays.sort(nums);
            
            int[] memo = new int[target + 1];
            memo[0] = 0;
            
            for(int i = 1; i <= target; i++){
                
                for(int j = 0; j < nums.length; j++){
                    if(i < nums[j]){
                        continue;
                    }
                    
                    if(i == nums[j]){
                        memo[i] += 1;
                    }
                    else{
                        int t = i - nums[j];
                        if(memo[t] != 0){
                            memo[i] += memo[t];
                        }
                    }
                }
                
            }
            
            return memo[target];
        }
    }
    

    646. Maximum Length of Pair Chain

    方法一 : 耗时47ms
    这是一道变型的Longest Increase Subsequence;注意,这里有二维数组的排序方法

    class Solution {
        public int findLongestChain(int[][] pairs) {
            int len = pairs.length;
            if(len < 2) return len;
            Integer[][] p = new Integer[len][2];
            for(int i = 0; i < len; i ++){
                p[i][0] = pairs[i][0];
                p[i][1] = pairs[i][1];
            }
            
            Arrays.sort(p, new Comparator<Integer[]>(){
                public int compare(Integer[] o1, Integer[] o2){
                    return o1[0] - o2[0];
                }
            });
            
            int[] dp = new int[len];
            dp[0] = 1;
            
            for(int i = 1; i < len; i ++){
                
                int maxi = 1;
                for(int j = 0; j < i; j ++){
                    if(p[j][1] < p[i][0]){
                        maxi = dp[j] + 1;
                    }
                }
                
                dp[i] = maxi;
            }
            
            return dp[len - 1];
        }
    }
    

    方法二 : 使用自己的快排,耗时3ms

    class Solution {
        public int findLongestChain(int[][] pairs) {
            if (pairs == null)
                return 0;
            int len = pairs.length;
            if (len < 2)
                return len;
            qsort(pairs, 0, len - 1);
            int sum = 1;
            int end = pairs[0][1];
            for (int i = 1; i < len; ++i) {
                if (pairs[i][0] > end) {
                    ++sum;
                    end = pairs[i][1];
                }
            }
            return sum;
        }
    
        private void qsort(int[][] pairs, int begin, int end) {
            if (begin >= end)
                return;
            int key = pairs[begin][1];
            int[] keyPair = pairs[begin];
            int i = begin, j = end;
            while(i < j) {
                while(i < j && key <= pairs[j][1])
                    --j;
                pairs[i] = pairs[j];
                while(i < j && key >= pairs[i][1])
                    ++i;
                pairs[j] = pairs[i];
            }
            pairs[i] = keyPair;
            qsort(pairs, begin, i - 1);
            qsort(pairs, i + 1, end);
        }
    }
    

    650. 2 Keys Keyboard

    方法一:递推 + memo
    还有递推方法,参考

    class Solution {
        public int minSteps(int n) {
            int[] dp = new int[n];
            dp[0] = 0;
            
            for(int i = 2; i <= n; i++){
                
                int halfi = i >> 1;
                int mini = i;
                for(int j = 2; j <= halfi; j++){
                    if(i % j == 0){
                        mini = Math.min(mini, dp[j-1] + 1 + i / j - 1);
                    }
                }
                dp[i-1] = mini;
            }
            
            return dp[n-1];
        }
    }
    

    376. Wiggle Subsequence :

    方法一: 递推 + memo
    这道题是LIS的变形,原理都一样

    class Solution {
        public int wiggleMaxLength(int[] nums) {
            if(nums.length == 0) return nums.length;
            
            int[][] dp = new int[nums.length][2];
            
            dp[0][0] = dp[0][1] = 1;
            
            for(int i = 1; i < nums.length; i ++){
                
                dp[i][0] = dp[i][1] = 1;
                for(int j = 0; j < i; j ++){
                    if(nums[i] > nums[j]){
                        dp[i][0] = Math.max(dp[i][0], dp[j][1] + 1);
                    }
                    if(nums[i] < nums[j]){
                        dp[i][1] = Math.max(dp[i][1], dp[j][0] + 1);
                    }
                }
            }
            
            return Math.max(dp[nums.length - 1][0], dp[nums.length - 1][1]);
        }
    }
    
  • 相关阅读:
    JPA + EclipseLink + SAP云平台 = 运行在云端的数据库应用
    Java实现 LeetCode 500 键盘行
    Java实现 LeetCode 500 键盘行
    Java实现 LeetCode 498 对角线遍历
    Java实现 LeetCode 498 对角线遍历
    Java实现 LeetCode 498 对角线遍历
    Java实现 LeetCode 496 下一个更大元素 I
    Java实现 LeetCode 496 下一个更大元素 I
    Java实现 LeetCode 496 下一个更大元素 I
    Java实现 LeetCode 495 提莫攻击
  • 原文地址:https://www.cnblogs.com/holidays/p/leetcode_note3.html
Copyright © 2011-2022 走看看