首页 > 技术文章 > 动态规划专题

yangcao 2019-11-17 11:46 原文

一、记忆化搜索

 斐波那契算法

1.递归代码(时间复杂度O(2^n)):

int f(int n){  
    if(n == 1 || n == 2){  
        return 1;  
    }  
    return f(n-1) + f(n-2);  
} 

2.递归加记忆化

public class Solution{
    int[] res = new int[n];
    Arrays.fill(res, -1);
int f(int n){ if(n == 1 || n == 2){ return 1; } if(res[n] == -1) res[n] = f(n-1) + f(n-2);
return res[n]; }
}

 

3.动态规划  或者 转化为非递归 或者转化为矩阵求解

 

递归是自上而下的解决问题(如求f(n)就已经假设所有小于n的值已经得到,)

动态规划是自下而上的解决问题(求f(n) 就已经把前面n-1个值完全获得)

动态规划   约等于  记忆化搜索 (严格来说不对 因而 记忆化搜索也是自上而下,动态规划是自下而上的解决问题 )

 

70. Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.

Example 1:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

代码如下:
class Solution {
    public int climbStairs(int n) {
        if(n ==1 || n ==2){
            return n;
        }else{
        int[] arr = new int[n];
        arr[0] = 1;
        arr[1] = 2;
        for(int i = 2; i < n; i++) {
            arr[i] = arr[i-1] + arr[i-2];
        }
        return arr[n-1];
      }
    }
}

 

120. Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

代码如下:

class Solution {
  public int minimumTotal(List<List<Integer>> triangle) {
      int[] re = new int[triangle.size() + 1];
      for(int i=triangle.size()-1;i>=0; i--){
          for(int j=0; j<triangle.get(i).size(); j++){
              re[j] = Math.min(re[j],re[j+1]) + triangle.get(i).get(j);//re数组每次填充的值都是一样的
          }
      }
      return re[0];
  }
}

 

64. Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example:

Input:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.

代码如下:
class Solution {
    public int minPathSum(int[][] grid) {
        
        for(int i=1; i<grid.length; i++) {
            grid[i][0] = grid[i-1][0] + grid[i][0];
        }
        for(int j=1; j<grid[0].length; j++) {
            grid[0][j] = grid[0][j-1] + grid[0][j];
        }
        for(int i=1; i<grid.length; i++) {
            for(int j=1; j<grid[0].length; j++) {
                grid[i][j] = Math.min(grid[i-1][j], grid[i][j-1]) + grid[i][j];
            }
        }
        return grid[grid.length-1][grid[0].length-1];
    }
}

 

343. Integer Break

Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.

Example 1:

Input: 2
Output: 1
Explanation: 2 = 1 + 1, 1 × 1 = 1.

Example 2:

Input: 10
Output: 36
Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36.
代码如下:
class Solution {
    public int integerBreak(int n) {
       int[] dp = new int[n + 1];
       dp[1] = 1;
       for(int i = 2; i <= n; i ++) {
           for(int j = 1; j < i; j ++) {
               dp[i] = Math.max(dp[i], (Math.max(j,dp[j])) * (Math.max(i - j, dp[i - j])));
           }
       }
       return dp[n];
    }
}

 

279. Perfect Squares

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.

Example 1:

Input: n = 12
Output: 3 
Explanation: 12 = 4 + 4 + 4.

Example 2:

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
可以使用BFS以及DP来解决问题
代码如下:
//BFS


class Solution {
  public int numSquares(int n) {
    Queue<Integer> q = new LinkedList<>();
    Set<Integer> visited = new HashSet<>();
    q.offer(0);
    visited.add(0);
    int depth = 0;
    while(!q.isEmpty()) {
        int size = q.size();
        depth++;
        while(size-- > 0) {
            int u = q.poll();
            for(int i = 1; i*i <= n; i++) {
                int v = u+i*i;
                if(v == n) {
                    return depth;
                }
                if(v > n) {
                    break;
                }
                if(!visited.contains(v)) {
                    q.offer(v);
                    visited.add(v);
                }
            }
        }
    }
    return depth;
  }
}
//DP
public int numSquares(int n) {
    int[] dp = new int[n + 1];
    Arrays.fill(dp, Integer.MAX_VALUE);
    dp[0] = 0;
    for(int i = 1; i <= n; ++i) {
        int min = Integer.MAX_VALUE;
        int j = 1;
        while(i - j*j >= 0) {
            min = Math.min(min, dp[i - j*j] + 1);
            ++j;
        }
        dp[i] = min;
    }        
    return dp[n];
}

 

91. Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given a non-empty string containing only digits, determine the total number of ways to decode it.

Example 1:

Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

代码如下:

class Solution {
    public int numDecodings(String s) {
        int[] dp = new  int[s.length()+1];
        dp[0] = 1;
        dp[1] = s.charAt(0) == '0' ? 0: 1;
        
        for(int i =2;i<=s.length();i++){
            int onedig = Integer.valueOf(s.substring(i-1,i));
            int twodig = Integer.valueOf(s.substring(i-2,i));
            if(onedig > 0){
                dp[i] += dp[i-1];
            }
            if(twodig >=10 && twodig <= 26){
                dp[i] += dp[i-2];
            }
            
        }
        return dp[s.length()];
    }
}


62. Unique Paths

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?

Example 1:

Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right

代码如下:

 

 

 

 

 

 

198. House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
             Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
             Total amount you can rob = 2 + 9 + 1 = 12.

代码如下:
//dp
public int rob(int[] num) {
    int[][] dp = new int[num.length + 1][2];
    for (int i = 1; i <= num.length; i++) {
        dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);
        dp[i][1] = num[i - 1] + dp[i - 1][0];
    }
    return Math.max(dp[num.length][0], dp[num.length][1]);
}


//非递归
class Solution {
   public int rob(int[] num) {
    int prevNo = 0;
    int prevYes = 0;
    for (int n : num) {
        int temp = prevNo;
        prevNo = Math.max(prevNo, prevYes);
        prevYes = n + temp;
    }
    return Math.max(prevNo, prevYes);
  }
}

 

 213. House Robber II

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle.That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [2,3,2]
Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2),
             because they are adjacent houses.

代码如下:
class Solution {
    public int rob(int[] nums) {
        if (nums.length == 1) return nums[0];
        return Math.max(rob(nums, 0, nums.length - 2), rob(nums, 1, nums.length - 1));
  }
    
    private int rob(int[] num, int lo, int hi) {
      int include = 0, exclude = 0;
      for (int j = lo; j <= hi; j++) {
          int i = include, e = exclude;
          include = e + num[j];
          exclude = Math.max(e, i);
     }
      return Math.max(include, exclude);
  }
   
}

 

 0 1 背包问题: https://blog.csdn.net/u013445530/article/details/40210587

代码如下:
public class Solution {

    public static int getMaxValue(int[] weight, int[] value, int w, int n) {
        int[][] table = new int[n + 1][w + 1];
        for (int i = 1; i <= n; i++) { //物品
            for (int j = 1; j <= w; j++) {  //背包大小
                if (weight[i] > j) {        
                        //当前物品i的重量比背包容量j大,装不下,肯定就是不装
                    table[i][j] = table[i - 1][j];
                    // System.out.print(table[i][j]+ " ");
                } else { //装得下,Max{装物品i, 不装物品i}
                    table[i][j] = Math.max(table[i - 1][j], table[i - 1][j - weight[i]] + value[i]);
                    //System.out.print(table[i][j]+ " ");
                }
            }
            // System.out.println();
        }
        return table[n][w];
    }

    public static void main(String[] args) {

        int n = 5, w = 10;                    //物品个数,背包容量
        int[] value = {0, 6, 3, 5, 4, 6};     //各个物品的价值
        int[] weight = {0, 2, 2, 6, 5, 4};    //各个物品的重量
        System.out.println(getMaxValue(weight,value,w,n));

    }
}

 

 



dp解决上升子序列问题

300. Longest Increasing Subsequence

Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 

Note:

  • There may be more than one LIS combination, it is only necessary for you to return the length.
  • Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

代码如下:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length == 0){
            return 0;
        }
       
        int[] dp = new int[nums.length];//含义dp[i]为:以nums[i]元素为结尾的最长上升子序列的数目
        Arrays.fill(dp,1);  //默自己为最长上升序列 所以默认1
int max = dp[0];//用一个max来记录,否则最后要for遍历一下 dp数组寻找最大值 for(int i=1; i<nums.length; i++){ for(int j=0; j<i; j++){ if(nums[j]<nums[i]){ dp[i] = Math.max(dp[i], dp[j]+1); //加1必须放在里面 } max = Math.max(max,dp[i]); } } return max; } }

 


376. Wiggle Subsequence

A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.

Example 1:

Input: [1,7,4,9,2,5]
Output: 6
Explanation: The entire sequence is a wiggle sequence.

Example 2:

Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
Explanation: There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
代码如下:










dp解决最长公共子序列问题







背包问题的应用(都是从数组或者其他里面选出来凑出一个什么东西) *******

416. Partition Equal Subset Sum

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].

代码如下

class Solution {
    public boolean canPartition(int[] nums) {
        if(nums==null || nums.length==0) {
            return false;
        }
        int sum = 0;
        for(int num : nums) {
            sum += num;
        }
        if(sum%2 != 0) {
            return false;
        }
        return this.subsetSum(nums, sum/2);
    }
    
    private boolean subsetSum(int[] nums, int target) {
        
        int row = nums.length, col = target + 1;
        boolean[][] dp = new boolean[row][col];
        dp[0][0] = true;
        for(int i=1; i<row; i++) {
            dp[i][0] = true;
        }
        for(int i=1; i<col; i++) {
            if(i == nums[0]) {
                dp[0][i] = true;
            }
        }
        for(int i=1; i<row; i++) {
            for(int j=1; j<col; j++) {
                if(j >= nums[i]) {
                    dp[i][j] = dp[i-1][j - nums[i]] || dp[i-1][j];
                } else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[row-1][col-1];
    }
}

 



322. Coin Change

 

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

代码如下:
class Solution {
public static int coinChange(int[] coins, int amount) {
    if (coins == null || coins.length == 0 || amount <= 0)
        return 0;
    int[] minNumber = new int[amount + 1];
    for (int i = 1; i <= amount; i++) {
        minNumber[i] = Integer.MAX_VALUE;//先赋值是为了后面比较
        for (int j = 0; j < coins.length; j++) {
            if (coins[j] <= i && minNumber[i - coins[j]] != Integer.MAX_VALUE)
                minNumber[i] = Integer.min(minNumber[i], 1 + minNumber[i - coins[j]]);
        }
    }
    if (minNumber[amount] == Integer.MAX_VALUE)
        return -1;
    else
        return minNumber[amount];
}
}

 

 

139. Word Break

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
代码如下:
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet<>(wordDict);
        Boolean[] dp = new Boolean[s.length()];
        return dfs(s, set, 0, dp);
    }
    
    private boolean dfs(String s, Set<String> dict, int idx, Boolean[] dp){
        if(idx == s.length()) return true;
        
        if(dp[idx] != null) return dp[idx];
        
        for(int i=idx+1; i<=s.length(); i++){
            if(dict.contains(s.substring(idx, i)) && dfs(s, dict, i, dp)){
                return dp[idx] = true;
            }
        }
        return dp[idx] = false;
    }
}

 















 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



 

推荐阅读