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];
    }
}

猜你喜欢

转载自www.cnblogs.com/holidays/p/leetcode_note3.html