Dynamic Programming(leetcode)持續更新

198. House Robber【Easy DP】
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.

時間O(N) 空間O(n)

這道題的實質就是找一個整數集里面的取出一個或多個不相鄰數,使其和最大。
首先會考慮Dynamic Programming的寫法。
Base Case: 第一家的錢 或者 第二家的錢 (the money in the first house or second house)
Induction Rule: M[i] 表示到i位置時不相鄰數能形成的最大和。 represents the max sum till the ith position. M[i] = Math.max(money[i] + M[i - 2], M[i - 1]);

public class Solution {
    public int rob(int[] nums) {
    int[] M = new int[nums.length];
    if(nums.length <= 1) {
        return nums.length == 0? 0: nums[0];
    }
    M[0] = nums[0];
    M[1] = Math.max(nums[0], nums[1]);
    for(int i = 2; i < nums.length; i++) {
        M[i] = Math.max( (nums[i] + M[i-2]), M[i-1]) ;
    }
    return M[nums.length - 1];
    }
}

可以進一步優化為O(1)的空間復雜度

public class Solution {
    public int rob(int[] nums) {
    if(nums.length <= 1) {
        return nums.length == 0? 0: nums[0];
    }
    int preMax = nums[0];
    int curMax = Math.max(nums[0], nums[1]);
    for(int i = 2; i < nums.length; i++) {
    int tem = b;
          b = Math.max( (nums[i] + a), b) ;
    }
    return b;
    }
}

213. House Robber II
Note: This is an extension of House Robber.
After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.
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.

**在上一題的基礎上,加了個條件,就是如果偷了第一家的錢,那么一定不能偷最后一家。分別算出搶第一家和不搶第一家這兩種情況下的收益,選最大
base case: 偷第一家, 或者偷第二家
Induction rule:M[i] 表示到i位置時不相鄰數能形成的最大和。 represents the max sum till the ith position. M[i] = Math.max(money[i] + M[i - 2], M[i - 1]);
最后的結果返回Math.max(M[1 - (n - 1)] , M[0 - (n-2)]); **
時間O(n) 空間O(1)

public class Solution {
    public int rob(int[] nums) {
        return Math.max(rob(nums, 0), rob(nums, 1) );
    }
    public int rob(int[] nums, int offset){
        if(nums.length <= 1+offset){
            return nums.length <= offset ? 0 : nums[0 + offset];
        }
        int preMax = nums[0 + offset];
        int curMax = Math.max( nums[1 + offset], nums[0 + offset]);
        for(int i  = 2 + offset; i < nums.length - 1 + offset; i++){
            int temp = curMax;
            curMax = Math.max(preMax + nums[i], curMax);
            preMax = temp;
        }
        return curMax;
    }
}

337. House Robber III
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
Example 1:
3
/ \
2 3
\
3 1

Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2:
3
/
4 5
/ \ \
1 3 1
Maximum amount of money the thief can rob = 4 + 5 = 9.
Native Solution:
Step I -- Think naively
At first glance, the problem exhibits the feature of "optimal substructure": if we want to rob maximum amount of money from current binary tree (rooted at root), we surely hope that we can do the same to its left and right subtrees.So going along this line, let's define the function rob(root) which will return the maximum amount of money that we can rob for the binary tree rooted at root; the key now is to construct the solution to the original problem from solutions to its subproblems, i.e., how to get rob(root) from rob(root.left),rob(root.right), ...
etc.
Apparently the analyses above suggest a recursive solution. And for recursion, it's always worthwhile figuring out the following two properties:
Termination condition: when do we know the answer to rob(root)
without any calculation? Of course when the tree is empty -- we've got nothing to rob so the amount of money is zero.

Recurrence rule: i.e., how to get rob(root)
from rob(root.left), rob(root.right), ...
etc. From the point of view of the tree root, there are only two scenarios at the end: root
is robbed or is not. If it is, due to the constraint that "we cannot rob any two directly-linked houses", the next level of subtrees that are available would be the four "
grandchild-subtrees
" (root.left.left, root.left.right, root.right.left, root.right.right). However if root
is not robbed, the next level of available subtrees would just be the two "child-subtrees" (root.left, root.right). We only need to choose the scenario which yields the larger amount of money.

Here is the program for the ideas above:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int rob(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int max = 0;
        if(root.left != null) {
            max += rob(root.left.left) + rob(root.left.right);
        }
        if(root.right != null) {
            max += rob(root.right.left) + rob(root.right.right);
        }
        return Math.max(rob(root.left) + rob(root.right), root.val +  max); 
    }
}

Step II -- Think one step further
In step I
, we only considered the aspect of "optimal substructure", but think little about the possibilities of overlapping of the subproblems. For example, to obtain rob(root), we need rob(root.left), rob(root.right), rob(root.left.left), rob(root.left.right), rob(root.right.left), rob(root.right.right); but to get rob(root.left), we also needrob(root.left.left), rob(root.left.right), similarly for rob(root.right). The naive solution above computed these subproblems repeatedly, which resulted in bad time performance. Now if you recall the two conditions for dynamic programming: "optimal substructure" + "overlapping of subproblems", we actually have a DP problem. A naive way to implement DP here is to use a hash map to record the results for visited subtrees.
And here is the improved solution:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int rob(TreeNode root) {

        return helper(root, new HashMap<>());
    }
    private int helper(TreeNode root, HashMap<TreeNode, Integer> myMap){
        if(root == null) {
            return 0;
        }
        if(myMap.containsKey(root)){
            return myMap.get(root);
        }
        int max = 0;
        if(root.left != null) {
            max += helper(root.left.left, myMap) + helper(root.left.right, myMap);
        }
        if(root.right != null) {
            max += helper(root.right.left, myMap) + helper(root.right.right, myMap);
        }
        max = Math.max(helper(root.left, myMap) + helper(root.right, myMap), root.val +  max); 
        myMap.put(root, max);
        return max;
    }
}

The runtime is sharply reduced to 9 ms, at the expense of O(n)
space cost (n is the total number of nodes; stack cost for recursion is not counted).
Step III -- Think one step backIn step I, we defined our problem as rob(root), which will yield the maximum amount of money that can be robbed of the binary tree rooted at root. This leads to the DP problem summarized in step II.
Now let's take one step back and ask why we have overlapping subproblems. If you trace all the way back to the beginning, you'll find the answer lies in the way how we have defined rob(root). As I mentioned, for each tree root, there are two scenarios: it is robbed or is not.rob(root) does not distinguish between these two cases, so "information is lost as the recursion goes deeper and deeper", which results in repeated subproblems.
If we were able to maintain the information about the two scenarios for each tree root,let's see how it plays out. Redefine rob(root) as a new function which will return an array of two elements, the first element of which denotes the maximum amount of money that can be robbed if root is not robbed, while the second element signifies the maximum amount of money robbed if it is robbed.Let's relate rob(root) to rob(root.left) and rob(root.right)..., etc. For the 1st element of rob(root), we only need to sum up the larger elements of rob(root.left) and rob(root.right), respectively, since root is not robbed and we are free to rob its left and right subtrees. For the 2nd element of rob(root), however, we only need to add up the 1st elements of rob(root.left) androb(root.right), respectively, plus the value robbed from root itself, since in this case it's guaranteed that we cannot rob the nodes ofroot.left and root.right.
As you can see, by keeping track of the information of both scenarios, we decoupled the subproblems and the solution essentially boiled down to a greedy one. Here is the program:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int rob(TreeNode root) {

       int[] res = helper(root);
        return Math.max(res[0], res[1]);
    }

    private int[] helper(TreeNode root) {
        if (root == null) return new int[2];
        
        int[] left = helper(root.left);
        int[] right = helper(root.right);
        int[] res = new int[2];
    
        res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        res[1] = root.val + left[0] + right[0];
        
        return res;
    }
}

55. Jump Game
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
使用一維DP linear scan 回頭看的思想
base case: 數組里面最后一個空位跳到本身需要的jump length
Induction Rule:M[i] 代表了 每次從 i 這個位置能否跳到最后或者是否能跳到一個為True的元素位置(跳到一個自身鞥跳到最后的位置)
if nums[i] >= n - i || nums[j] = true (j = i 到n之間) M[i] = true

public boolean canJump(int[] array) {
     boolean[] canJumap = new boolean[array.length];
     canJump[0] = true;
     for(int i = 1; i < array.length; i++) {
         for(int j = 0; j < i; j++) {
             if(canJump[j] && array[j] + j >= i) {
                 canJump[i] = true;
                 break;
             }
        }
  }
  return canJump(array.length - 1);
}

53. Maximum Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.
// 用dp解題
//保存一個
//induction rule : if sum > 0 -> sum += array[i] else -> sum = array[i]
//base case is the first one integer: M[0] = array[0]
[-2,1,-3,4,-1,2,1,-5,4]
-2 1 -3 4 3 5 6 6 6

public int maxSubArray(int[] nums) {
         if(nums == null) {
             return 0;
         }
         int max = nums[0];
         int result = nums[0];
         for(int i = 1; i < nums.length; i++) {
               if(max >= 0){
                  max += nums[i];   
               }else{
                  max = nums[i];
               }
             result = Math.max(max, result);
         }
         return result;
}
  1. Range Sum Query - Immutable
    Given an integer array nums, find the sum of the elements between indices i and j (ij), inclusive.
    Example:
    Given nums = [-2, 0, 3, -5, 2, -1]sumRange(0, 2) -> 1sumRange(2, 5) -> -1sumRange(0, 5) -> -3

Note:
You may assume that the array does not change.
There are many calls to sumRange function.
//Can use DP or DFS
DP解法:
用一個一維數組存入到每個整數的prefix sum

public class NumArray {
    int[] M;
    public NumArray(int[] nums) {
        for(int i = 1; i < nums.length; i++) {
            nums[i] = nums[i - 1] + nums[i];
        }
        this.M = nums;
    }

    public int sumRange(int i, int j) {
        if(i == 0) {
            return M[j];
        }
        return M[j] - M[i - 1];
    }
}

276.Paint Fence
There is a fence with n posts, each post can be painted with one of the k colors.

You have to paint all the posts such that no more than two adjacent fence posts have the same color.

Return the total number of ways you can paint the fence.

Note: n and k are non-negative integers.
base case : the first wall
1 2

public class Solution {
    public int numWays(int n, int k) {
        if (n == 0 || k == 0)
            return 0;
        if (n == 1)
            return k;
        int same_count = k;
        int differ_count = k * (k - 1);
        for (int i = 3; i <= n; i++) {
            int temp = differ_count;
            differ_count = differ_count * (k - 1) + same_count * (k - 1);
            same_count = temp;
        }
        return same_count + differ_count;
    }
}

121. Best Time to Buy and Sell Stock
Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Example 1:
Input: [7, 1, 5, 3, 6, 4]Output: 5max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)

Example 2:
Input: [7, 6, 4, 3, 1]Output: 0In this case, no transaction is done, i.e. max profit = 0.
//此題很簡單,只需要遍歷一次數組,用一個變量記錄遍歷過數中的最小值,然后每次計算當前值和這個最小值之間的差值最為利潤,然后每次選較大的利潤來更新。當遍歷完成后當前利潤即為所求,代碼如下:

public class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length <= 1){
            return 0;
        }
        int max = 0;
        int min = prices[0];
        for(int i = 1; i < prices.length; i++) {
            min = Math.min(min, prices[i]);
            max = Math.max(prices[i] - min, max);
        }
        return max;
    }
}

309. Best Time to Buy and Sell Stock with Cooldown
Say you have an array for which the ith element is the price of a given stock on day i.Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)

Example:
prices = [1, 2, 3, 0, 2]maxProfit = 3transactions = [buy, sell, cooldown, buy, sell]

The natural states for this problem is the 3 possible transactions : buy, sell, rest. Here rest means no transaction on that day (aka cooldown). Then the transaction sequences can end with any of these three states. For each of them we make an array, buy[n], sell[n] and rest[n].
buy[i] means before day i
what is the maxProfit for any sequence end with buy.
sell[i] means before day i what is the maxProfit for any sequence end with sell.
rest[i] means before day i what is the maxProfit for any sequence end with rest.
Then we want to deduce the transition functions for buy sell and rest. By definition we have:
buy[i] = max(rest[i-1]-price, buy[i-1]) sell[i] = max(buy[i-1]+price, sell[i-1])rest[i] = max(sell[i-1], buy[i-1], rest[i-1])

Where price
is the price of day i
. All of these are very straightforward. They simply represents :
(1) We have to rest before we buy and (2) we have to buy before we sell

One tricky point is how do you make sure you sell before you buy, since from the equations it seems that [buy, rest, buy] is entirely possible. Well, the answer lies within the fact that buy[i] <= rest[i] which means rest[i] = max(sell[i-1], rest[i-1]). That made sure [buy, rest, buy] is never occurred. A further observation is that and rest[i] <= sell[i] is also true therefore rest[i] = sell[i-1] Substitute this in to buy[i] we now have 2 functions instead of 3:

_
buy[i] = max(sell[i-2]-price, buy[i-1])
sell[i] = max(buy[i-1]+price, sell[i-1])
_

This is better than 3, but
we can do even better
Since states of day i relies only on i-1and i-2we can reduce the O(n) space to O(1). And here we are at our final solution:

public class Solution {
   public int maxProfit(int[] prices) {
       int sell = 0, prev_sell = 0, buy = Integer.MIN_VALUE, prev_buy;
       for (int price : prices) {
           prev_buy = buy;
           buy = Math.max(prev_sell - price, prev_buy);
           prev_sell = sell;
           sell = Math.max(prev_buy + price, prev_sell);
       }
       return sell;
   }
}

122. Best Time to Buy and Sell Stock II
Say you have an array for which the ith element is the price of a given stock on day i.Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
這題只需要loop整個prices數組,只需要從第二天開始,如果當前價格比之前價格高,則把差值加入利潤中,因為我們可以昨天買入,今日賣出,若明日價更高的話,還可以今日買入,明日再拋出。以此類推,遍歷完整個數組后即可求得最大利潤。代碼如下:

public class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0){
            return 0;
        }
        int profit = 0;
        for(int i = 1; i < prices.length; i++) {
            if(prices[i] > prices[i - 1]) {
                profit += prices[i] - prices[i-1];
            }
        }
        return profit;
    }
}
  1. Best Time to Buy and Sell Stock III
    Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most two transactions.
    Note:You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
    //可以以每個price為分界點,把price分為兩個部分,把第一部分的最大profit加上第二部分的profit放到M, 返回最大的profit和
    //M[i]代表了以i為分界點的左右兩邊最大profit的和
    //M[0]等于整個int array 的最大profit
    public int maxProfit(int[] prices) {
        if (prices.length == 0){
            return 0;
        }
        int[] profitUntil = new int[prices.length];
        int[] profitFrom = new int[prices.length];
        //Calculate the profit until i.
        int minValue = prices[0];
        profitUntil[0] = 0;
        for (int i = 1; i < prices.length; i++) {
            minValue = Math.min(minValue, prices[i]);
            profitUntil[i] = Math.max(profitUntil[i - 1], prices[i] - minValue);
        }
        //Calculate the profit from i.
        profitFrom[prices.length - 1] = 0;
        int maxValue = prices[prices.length - 1];
        for (int i = prices.length - 2; i >= 0; i--) {
            maxValue = Math.max(maxValue, prices[i]);
            profitFrom[i] = Math.max(profitFrom[i + 1], maxValue - prices[i]);
        }
        int maxProfit = 0;
        for (int i = 0; i < prices.length; i++){
            maxProfit = Math.max(maxProfit, profitUntil[i] + profitFrom[i]);
        }
        return maxProfit;
      }
    }
    

375. Guess Number Higher or Lower II
We are playing the Guess Game. The game is as follows:

I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I'll tell you whether the number I picked is higher or lower.
However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.
Example:
n = 10, I pick 8.First round: You guess 5, I tell you that it's higher. You pay $5.Second round: You guess 7, I tell you that it's higher. You pay $7.Third round: You guess 9, I tell you that it's lower. You pay $9.Game over. 8 is the number I picked.You end up paying $5 + $7 + $9 = $21.

Given a particular n ≥ 1, find out how much money you need to have to guarantee a win.

public class Solution {
    public int getMoneyAmount(int n) {
        int[][] M = new int[n + 1][n + 1];
        return DP(M, 0, n);
    }
    private int DP(int[][] M, int start, int end) {
        if(start >= end) {
            return 0;
        }
        if(M[start][end] != 0) {
            return M[start][end];
        }
        int res = Integer.MAX_VALUE;
        for(int i = start; i <= end; i++) {
            int temp = i + Math.max(DP(M, start, i - 1), DP(M, i+1, end));
            res = Math.min(res,temp);
        }
        M[start][end] = res;
        return res;
    }
}

264. Ugly Number II
Write a program to find the n-th ugly number.Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
Note that 1 is typically treated as an ugly number.
Hint:
The naive approach is to call isUgly for every number until you reach the nth one. Most numbers are not ugly. Try to focus your effort on generating only the ugly ones. An ugly number must be multiplied by either 2, 3, or 5 from a smaller ugly number.
The key is how to maintain the order of the ugly numbers. Try a similar approach of merging from three sorted lists: L1, L2, and L3.
Assume you have Uk, the kth ugly number. Then Uk+1 must be Min(L1 * 2, L2 * 3, L3 * 5).

    public int nthUglyNumber(int n) {
        //ungly number base case: 2 3 5
        
        int[] M = new int[n];
        M[0] = 1;
        int indexTwo = 0, indexThree = 0, indexFive = 0;
        int factorTwo = 2, factorThree = 3, factorFive = 5;
        for(int i = 1; i < n; i++) {
            int min = Math.min(Math.min( factorTwo, factorThree) , factorFive);
            M[i] = min;
            if(factorTwo == min) {
                factorTwo = 2 * M[++indexTwo];
            }
            if(factorThree == min) {
                factorThree = 3 * M[++indexThree];
            }
            if(factorFive == min) {
                factorFive = 5 * M[++indexFive];
            }
        }
        return M[n - 1];
    }

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.

這道題跟 Dungeon Game 很像,用動態規劃Dynamic Programming來做,維護一個二維數組,其中M[i][j]表示當前位置的最小路徑和,遞推式 M[i][j] = M[i][j] + min(M[i - 1][j], 代碼如下:

public class Solution {
    public int minPathSum(int[][] grid) {
        //M[i][j] represents the minimum sum from this point to the bottom right
        int m = grid.length;
        int n = grid[0].length;
        int[][] M = new int[m][n];
        M[0][0] = grid[0][0];
        for(int i = 1; i < n; i++){
            M[0][i] = M[0][i - 1] + grid[0][i];
        }
        for(int i = 1; i < m; i++){
            M[i][0] += M[i - 1][0] + grid[i][0];
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                M[i][j] = Math.min(M[i-1][j] , M[i][j - 1]) + grid[i][j];
            }
        }
        return M[m - 1][n - 1];
    }
}

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?

UniquePaths.png

Above is a 3 x 7 grid. How many possible unique paths are there?
Note: m and n will be at most 100.

induction_rule.png

**base case: 當i= 0, M[i][j] = 1 當 j = 0, M[i][j] = 1;
M[i][j] = M[i - 1][j] + M[i][j -1]
**

public class Solution {
    public int uniquePaths(int m, int n) {
        int[][] M = new int[m][n];
        M[0][0] = 1;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(i == 0 || j == 0){
                    M[i][j] = 1;
                }else{
                    M[i][j] = M[i - 1][j] + M[i][j - 1];
                }
            }
        }
        return M[m - 1][n - 1];
    }
}

63. Unique Paths II
Follow up for "Unique Paths":
Now consider if some obstacles are added to the grids. How many unique paths would there be?An obstacle and empty space is marked as 1 and 0 respectively in the grid.
For example,
There is one obstacle in the middle of a 3x3 grid as illustrated below.
[
[0,0,0],
[0,1,0],
[0,0,0]
]
The total number of unique paths is 2.
Note: m and n will be at most 100.

在路徑中加了一些障礙物,用動態規劃Dynamic Programming來解,不同的是當遇到為1的點,將該位置的dp數組中的值清零,其余和unique path I差不多,代碼如下:

public class Solution {
   public int uniquePathsWithObstacles(int[][] obstacleGrid) {
       if (obstacleGrid == null || obstacleGrid.length == 0 || obstacleGrid[0].length == 0) {
           return 0;
       }
       int m = obstacleGrid.length;
       int n = obstacleGrid[0].length;
       int[][] M = new int[m][n];
     
       for(int i = 0; i < m; i++){
           if(obstacleGrid[i][0] != 1){
               M[i][0] = 1;
           }else{
               break;
           }
       }
       for(int j = 0; j < n; j++){
           if(obstacleGrid[0][j] != 1){
               M[0][j] = 1;
           }else{
               break;
           }
       }
       for(int i = 1; i < m; i++){
           for(int j = 1; j < n; j++){
               if(obstacleGrid[i][j] == 1){
                   M[i][j] = 0;
               }else{
                   M[i][j] = M[i -1][j] + M[i][j - 1];
               }
           }
       }
       return M[m - 1][n - 1];
   }
}

464. Can I Win !!!!
In the "100 game," two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.
What if we change the game so that players cannot re-use integers?
For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.
Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally.
You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300.
Example
Input:
maxChoosableInteger = 10
desiredTotal = 11
Output:
false
Explanation:
No matter which integer the first player choose, the first player will lose.The first player can choose an integer from 1 up to 10.If the first player choose 1, the second player can only choose integers from 2 up to 10.The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.Same with other integers chosen by the first player, the second player will always win.
記憶化搜索 + 位運算

由于maxChoosableInteger不大于20,因此可以通過整數state表示當前已經選擇了哪些數字, state的第i位為1時,表示選擇了數字i + 1,利用字典dp記錄已經搜索過的狀態

public class Solution {

    private Boolean[] win;
    int choosen = 0;

    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        if (desiredTotal == 0) {
            return true;
        }
        if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) {
            return false;
        }
        win = new Boolean[1 << maxChoosableInteger];
        return canWin(maxChoosableInteger, desiredTotal, 0);
    }

    private boolean canWin(int n, int total, int now) {
        if (win[choosen] != null)
            return win[choosen];
        if (now >= total) {
            win[choosen] = false;
            return false;
        }
        for (int i = 1; i <= n; i++) {
            int bit = 1 << (i - 1);
            if ((choosen & bit) == 0) {
                choosen ^= bit;
                boolean ulose = !canWin(n, total, now + i);
                choosen ^= bit;

                if (ulose) {
                    win[choosen] = true;
                    return true;
                }
            }
        }
        win[choosen] = false;
        return false;
    }
}

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:coins = [1, 2, 5], amount = 11 return 3 (11 = 5 + 5 + 1)
Example 2:coins = [2], amount = 3 return -1.
Note:You may assume that you have an infinite number of each kind of coin.

維護一個一維動態數組M,其中M[i]表示錢數為i時的最小硬幣數的找零,遞推式為:
M[i] = min(M[i], M[i - coins[j]] + 1);
其中coins[j]為第j個硬幣,而i - coins[j]為錢數i減去其中一個硬幣的值,剩余的錢數在M數組中找到值,然后加1和當前M數組中的值做比較,取較小的那個更新dp數組

public class Solution {
    
    public int coinChange(int[] coins, int amount) {
        if(amount<1) return 0;
        int[] M = new int[amount+1];
        int sum = 0;
        while(++sum<=amount) {
            int min = -1;
            for(int coin : coins) {
                if(sum >= coin && M[sum-coin]!=-1) {
                    int temp = M[sum-coin]+1;
                    min = min<0 ? temp : (temp < min ? temp : min);
                }
            }
            M[sum] = min; 
        }
        return M[amount];
    }
}

338. Counting Bits
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.
Example:
For num = 5 you should return [0,1,1,2,1,2].
Follow up:
It is very easy to come up with a solution with run time O(nsizeof(integer))*. But can you do it in linear time O(n) /possibly in a single pass?
Space complexity should be O(n).
Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.

當一個數為2的整數冪的時候,1的個數為1,比如2(10) 和4(100),8(1000)

在這之后就是前一個序列的數+1 比如 9(1001) = 1(1) + 8 (1) = 2

就是把一個數分解為小于它的最大2的整數冪 + x

public class Solution {
    public int[] countBits(int num) {
    
        int[] res = new int[num+1];
        int pow2 = 1,before =1;
        for(int i=1;i<=num;i++){
            if (i == pow2){
                before = res[i] = 1;
                pow2 <<= 1;
            }
            else{
                res[i] = res[before] + 1;
                before += 1;
            }
        }
        return res;

    }
}

方法2
解題思路:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

10 1010

11 1011

12 1100

13 1101

14 1110

15 1111

........

觀察上面的情況,我們發現0,1,2-3,4-7,8-15為一組,且每組開頭的1的位數都是1。每組其余的數都可以用本組開頭的數加上另一個差值。且這兩個數都已經在前面算過了。

    public int[] countBits(int num) {
    //當i為2的倍數時,只有一個1. 當不為2的倍數時,等于result[i-1] + 1;
        if (num < 0) {
            return new int[0];
        }
         
        int[] result = new int[num + 1];
         
        for (int i = 1; i <= num; i++) {
            if ((i & 1) == 0) {
                result[i] = result[i / 2];
            } else {
                result[i] = result[i - 1] + 1;
            }
        }
         
        return result;
    }

95. Unique Binary Search Trees II **
Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1...
n
.
For example,Given n = 3, your program should return all 5 unique BST's shown below.

UniqueBinarySearchTrees.png
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<TreeNode> generateTrees(int n) {
        //取任意一個數為root
        if(n == 0){
            return new ArrayList<TreeNode>();
        }
        return generate(1, n);
    }
    private List<TreeNode> generate(int s, int e) {
        List<TreeNode> result = new ArrayList<>();
        if(s > e) {
            result.add(null);
            return result;
        }
        
        for(int i = s; i <= e; i++) {
            List<TreeNode> leftNode = generate(s, i - 1);
            List<TreeNode> rightNode = generate(i + 1, e);
            for(TreeNode l : leftNode){
                for(TreeNode r : rightNode){
                    TreeNode root = new TreeNode(i);
                    root.left = l;
                    root.right = r;
                    result.add(root);
                }
            }
        }
        return result;
    }
}

96. Unique Binary Search Trees
Given n, how many structurally unique BST's (binary search trees) that store values 1...n?
For example,Given n = 3, there are a total of 5 unique BST's.

UniqueBinarySearchTrees.png

首先注意這里是BST而不是普通的Binary Tree,所以數字會對插入的位置有影響。這類找combination/permutation的題都需要找找規律。

n = 0

n = 1
1

n = 2

   1                  2
     \               /
      2            1

n = 3

   1          3    3        2     1
    \        /     /       / \     \
     3     2      1       1   3     2
    /     /        \                 \
   2     1          2                 3

定義f(n)為unique BST的數量,以n = 3為例:

構造的BST的根節點可以取{1, 2, 3}中的任一數字。

如以1為節點,則left subtree只能有0個節點,而right subtree有2, 3兩個節點。所以left/right subtree一共的combination數量為:f(0) * f(2) = 2

以2為節點,則left subtree只能為1,right subtree只能為2:f(1) * f(1) = 1

以3為節點,則left subtree有1, 2兩個節點,right subtree有0個節點:f(2)*f(0) = 2

總結規律:
f(0) = 1
f(n) = f(0)f(n-1) + f(1)f(n-2) + ... + f(n-2)f(1) + f(n-1)f(0)**

public class Solution {
    //f(n) = f(0)*f(n -1) + f(1)*f(n - 2) + ... + f(n - 1)f(0)
    public int numTrees(int n) {
        int[] M = new int[n + 1];
        //M[i] represent the number of BST when there are i nodes
        //M[0] represent the number of BST when there are 0 node, which is 1 BST can be build when there is only one node
        M[0] = 1;
        //1 BST can be build when there is only one node
        M[1] = 1;
        for(int i = 2; i <= n; i++) {
            for(int j = 0; j < i; j++){
                M[i] += M[j] * M[i - j - 1];
            }
        }
        return M[n];
    }
}

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

DecodeWays.png

Given an encoded message containing digits, determine the total number of ways to decode it.
For example,Given encoded message "12", it could be decoded as "AB"
(1 2) or "L" (12).
The number of ways decoding "12" is 2.

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.
For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).
Note: You may assume that n is not less than 2 and not larger than 58.
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

Triangle.png

LC:
**300. Given an array A[0]...A[n-1] of integers, find out the length of the longest ascending subsequence.
Assumptions
A is not null

ExamplesInput: A = {5, 2, 6, 3, 4, 7, 5}Output: 4Because [2, 3, 4, 5] is the longest ascending subsequence.**
方法一
O(n^2)
任意一個位置往前面看所有的數,找到比當前的數小的最大數的值M[j] + 1 (Math.max(M[i], M[j] + 1)

public class Solution {
  public int longest(int[] array) {
    // Write your solution here.
    //using dp to solve this problem
    //M[i] represents the longset ascending subsequence from 0 to i
    //base case is the first element
    //Input: A = {5, 2, 6, 3, 4, 7, 5}
        //        1   1  2  2  3  4  4
    int max = 1;
    int[] M = new int[array.length];
    if(array.length == 0){
      return 0;
    }
   
    for(int i = 0; i < array.length; i++){
      M[i] = 1;
      for(int j = 0; j < i; j++) {
        if(array[i] > array[j]){
          M[i] = Math.max(M[i], M[j] + 1);
        }
      }
      max = Math.max(M[i], max);
    }
    return max;
  }
}

方法二:
從第0 個元素開始往數組里面插入元素, 如果往前找,如果能找到比插入的當前元素大的最小的元素,則用當前的元素替換掉那個數; 如果找不到,直接再數組后面插入這個數。返回數組長度

O(nlogn)

public class Solution {
    public int lengthOfLIS(int[] nums) {
  //step1: find the largest smaller number of int i;Using binary search
  //{5,2,3,1,9,4,2,1,5,7,8}
  // 1 2 4 5 7 8     
    if(nums.length == 0) {
      return 0;
    }
    int[] M = new int[nums.length + 1];
    M[1] = nums[0];//2
    int result = 1;
    for(int i = 1; i < nums.length; i++){
        int index = largestSmaller(M, 1, result, nums[i]);//
        if(index == result){
            M[++result] = nums[i];
        }else{
            M[index + 1] = nums[i];
        }
        System.out.print(M[result] +",");
    }
    return result;
  }
  
    private int largestSmaller(int[] M, int left, int right, int target){
        while(left <= right) {
            int mid = left + (right - left)/2;
            if(M[mid] >= target){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
            return right;
        }
}

LC
**Given an array of 2D coordinates of points (all the coordinates are integers), find the largest number of points that can be crossed by a single line in 2D space.
Assumptions
The given array is not null and it has at least 2 points

Examples
<0, 0>, <1, 1>, <2, 3>, <3, 3>, the maximum number of points on a line is 3(<0, 0>, <1, 1>, <3, 3> are on the same line)**

在這里使用線的點斜式定義,遍歷所有的點,找到每個點和其他所有點組成的線的斜率,若相等,則是在同一條線上,記錄下斜率相同的pair個數。數據結構:使用hashMap來儲存每個可能的slope
/*
* class Point {
*   public int x;
*   public int y;
*   public Point(int x, int y) {
*     this.x = x;
*     this.y = y;
*   }
* }
*/
public class Solution {
  public int most(Point[] points) {
    // Write your solution here.
    int result = 0;
    for(int i = 0; i < points.length; i++){
      Point seed = points[i];
      int same = 1;
      int sameX = 0;
      int most = 0;
      HashMap<Double, Integer> cnt = new HashMap<>();
      for(int j = 0; j < points.length; j ++){
        if(i == j){
          continue;
        }
        Point temp = points[j];
        //斜率和截距都相等
        if(temp.x == seed.x && temp.y == seed.y){
          same++;
        }else if(temp.x == seed.x){
          sameX++;
        }else{
          //斜率相等,過同一點的直線上的點
          double slope = ((temp.y - seed.y) + 0.0)/(temp.x -seed.x);
          if(!cnt.containsKey(slope)){
            cnt.put(slope, 1);
          }else{
            cnt.put(slope, cnt.get(slope)+1);
          }
          most = Math.max(most, cnt.get(slope));
        }
        
      }
      most = Math.max(most, sameX) + same;
      result = Math.max(result, most);
    }
      return result;
  }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容