[golang] leetcode intermediate - Change Exchange & longest rising subsequence

Question 1 Change

subject

Greedy (invalid)

In daily life, if we want to exchange change, the most simple thinking is naturally

If a coin with a larger denomination can be used, it is preferred

This is the thinking of greedy algorithm

code

func coinChange(coins []int, amount int) int {
    var res int
    sort.Ints(coins)
    if amount==0{return 0}
    for amount>=coins[0]{
        index:=sort.SearchInts(coins,amount)
        if index==len(coins){
            amount-=coins[len(coins)-1]
            res++
            continue
        }
        if coins[index]!=amount {
            amount-=coins[index-1]
        }else{
            amount-=coins[index]
        }
        res++
    }
    if amount!=0 {return -1}
    return res
}

The solution can pass the test case

But it cannot be submitted for approval

The reason is because

The coin combination provided in the title is not necessarily the coin combination used in daily life

Coins with large denominations are not necessarily local optimal solutions

Some numbers that can be composed of a combination of small face values cannot be represented by large face values

If you want to continue to use the greedy algorithm, you need to add a backtracking statement to exclude the scheme using large face value solution, and then continue the search

Because the efficiency is too low, we will not continue to optimize here

For subsequent optimization ideas, please refer to the solution of selected questions

https://leetcode-cn.com/probl...

dynamic programming

We can find

The process of finding coin combinations

In essence, it can be disassembled into the result of selecting coins one by one

For example:

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

The problem-solving process of dynamic programming is essentially the process of solving dp[11]

When choosing the first coin, there are three options
Select Coin 1. The number of coins is dp[10]+1
Select coin 2. The number of coins is dp[9]+1
Choose 5 coins, and the number of coins is dp[6]+1
Among the three schemes, the scheme with the smallest number is the solution of dp[11]

So we can get
dp[amount]=min{dp[amount-coins[0],dp[amount-coins[1],...,dp[amount-coins[n-1]}+1

The detailed code is

func coinChange(coins []int, amount int) int {
    //Initialize the array. Since coins are of integer type, the maximum number of selected coin combinations cannot be greater than the face value amount
    max := amount + 1
    dp :=make([]int,max)
    for i:=range dp{
        dp[i]=max
    }
    dp[0] = 0

    for i := 1; i <max; i++ {
        for j := 0; j < len(coins); j++ {
            if coins[j] <= i {
            dp[i] = min(dp[i], dp[i - coins[j]] + 1)
            }
        }
    }

    if dp[amount]>amount {
        return -1
    }else{
        return dp[amount]
    }
}
func min(x int,y int)int{
    if x<y{
        return x
    }else {
        return y
    }
}

Complexity analysis

Time complexity: O(Sn), where s is the amount of money and N is the denomination len(coins). We need to calculate a total of O(S) states. S is the total amount given by the topic. For each state, n denominations need to be enumerated each time to transfer the state, so a total of O(Sn) time complexity is required.
Space complexity: O(S). Array dp requires space with a length of total amount S.

The longest ascending subsequence of question 2

subject

thinking

The search process of subsequence obviously has the most substructure property

For i-length sequences

The optimal solution of the first i-1 subsequence starting with the first element can be calculated

The solution of dp[i] is the longest one plus one of all the local optimal solutions that satisfy the increment

code

func lengthOfLIS(nums []int) int {
    if len(nums) == 0 {
        return 0
    }
    dp := make([]int, len(nums))
    dp[0] = 1
    maxans := 1
    for i := 1; i < len(nums); i++ {
        dp[i] = 1
        for j := 0; j < i; j++ {
            if nums[i] > nums[j] {
                dp[i] = max(dp[i], dp[j]+1)
            }
        }
        maxans = max(maxans, dp[i])
    }
    return maxans
}
func max(x int,y int)int{
    if x>y{
        return x
    }else{
        return y
    }
}

Complexity analysis

Time complexity: O(n^2), where n is the length of array nums. The number of states of dynamic programming is n. when calculating state dp[i], it takes O(n) time to traverse all States of dp[0... I − 1], so the total time complexity is O(n^2)

Space complexity: O(n), additional dp array with length n is required.

Optimization - greed + dichotomy

Reference official solution
https://leetcode-cn.com/probl...
Ideas and algorithms

Consider a simple greedy, if we want to make the ascending subsequence as long as possible, we need to make the sequence rise as slowly as possible, so we want the number added at the end of the ascending subsequence to be as small as possible.

Based on the above greedy idea, we maintain an array d[i], which represents the minimum value of the last element of the longest ascending subsequence with length I, and use L to record the length of the current longest ascending subsequence. At the beginning, l is 1, d[1]=nums[0].

At the same time, we can notice that d[i] is monotonically increasing about I. Because if d[j] ≥ d[i] and j < I, we consider deleting I − J elements from the end of the longest ascending subsequence with length I, then the sequence length becomes J, and the jth element x (tail element) must be less than d[i], that is, less than d[j]. Then we find the longest ascending subsequence with length J, and the end element is smaller than d[j], resulting in contradiction. Therefore, the monotonicity of Array D is proved.

We iterate through each element in the array nums in turn and update the values of arrays D and l. If num [i] > d [len], update len=len+1, otherwise find the subscript i satisfying d[i − 1] < num [J] < d[i] in d[1... Len], and update d[i] = num [J].

According to the monotonicity of d array, we can use binary search to find the subscript i and optimize the time complexity.

Finally, the whole algorithm flow is as follows:

Let the length of the longest ascending subsequence currently calculated be len (initially 1), traverse the array nums from front to back, and when traversing to nums[i]:

If num [i] > d [len], add it directly to the end of D array and update len=len+1;

Otherwise, binary search in the D array, find the first number d[k] smaller than nums[i], and update d[k+1]=nums[i].

Take the input sequence [0, 8, 4, 12, 2] as an example:

Step 1: insert 0, d = [0];

Step 2 insert 8, d = [0, 8]

Step 3 insert 4, d = [0, 4]

Step 4 insert 12, d = [0, 4, 12]

Step 5 insert 2, d = [0, 2, 12]

Finally, the maximum increasing subsequence length is 3.

code

func lengthOfLIS(nums []int) int {
    l := 1
    n := len(nums)
    if n == 0 {
        return 0
    }
    d:=make([]int,n+1)
    d[l] = nums[0]
    for i := 1; i < n; i++ {
        if nums[i] > d[l] {
            l++
            d[l] = nums[i]
        } else {
            ll := 1
            rr := l
            pos := 0 // If it cannot be found, it means that all numbers are larger than nums[i], and d[1] needs to be updated at this time, so pos is set to 0 here
            for ll <= rr {
                mid := (ll + rr) >> 1
                if d[mid] < nums[i] {
                    pos = mid
                    ll = mid + 1
                } else {
                    rr = mid - 1
                }
            }
            d[pos + 1] = nums[i]
        }
    }
    return l
}

Complexity analysis

Time complexity: O(nlogn). The length of array nums is n. We use the elements in the array to update the d array in turn. When updating the d array, we need to conduct a binary search of O(logn), so the total time complexity is O(nlogn).

Space complexity: O(n), additional d array with length n is required.

Keywords: Go leetcode

Added by unreal128 on Tue, 22 Feb 2022 14:20:05 +0200