[Summary] Dynamic Programming

Dynamic programming method (dynamic programming) commonly used to solve the optimization problem (optimization problem), it applies to those cases where the overlapping sub-problems, that is not an independent sub-problems, problems have different sub sub sub common problem (the problem is the son sub-problems). This is obviously the divide and conquer method is different, divide and conquer the problem into sub-problems that do not overlap, then these sub-problems are solved, these problems will finally merge to give a final solution.
In the case of a common sub-problems, divide and conquer will do a lot of unnecessary work, it will be solved in the same sub-sub-problems many times. Dynamic programming is not the same, it will only be solved once for each sub-sub-problems, save it in a table, to avoid unnecessary duplication of calculation.
The use of dynamic programming is to seek out an optimal solution to this problem (an optimal solution), keep in mind here is just the optimal solution to solve a (the optimal solution) is because there may be more optimal solution.
Dp issue applies must meet the optimization theory and no after-effect.
1. The principle of optimization : If the optimal solution contains sub-optimal solution is the solution of the problem, the problem is said to have the most substructure that meet the optimization principle. (That is, after a certain optimal substructure by choosing the best)
2. No after-effect : Once the status of a stage, after which the evolutionary process is no longer influenced by previous decisions of the various states and, simply put, is "the future has nothing to do with the past", the current state is after a complete summary of the history, previous history only to influence the future evolution of the process by the current state.
3. The overlapping sub-problems: That is not independent of each sub-problem, a problem child in the next phase of decision-making may be used multiple times to. (This property is not dynamic programming necessary conditions apply, but if this is not the nature of dynamic programming algorithm does not have the advantage compared with other algorithms). In fact, dp is essentially a space for time approach, in order to reduce the complexity of the time, no sub-repeat calculation problem, various states which must be stored in the process.

Dynamic programming can be gradually optimize enumeration from violence. Various methods of problem solving: violence enumeration -> recursive -> memo algorithm -> move normalization algorithm

[leetcode]10.Regular Expression Matching

For a p-bit strings, letters, point \ (. \) , The letter \ (* \) four elements, a point matches any letter of the alphabet, the letter matching the same, point \ (* \) matches any letter (which may be any different letters, for example, \ (. * \) matching ABC), letters \ (* \) matches any consecutive identical letters, is worth noting that \ (* \) any including zero. Since \ (* \) can match any number, whether caused when the test s and p exact match is difficult to determine exactly \ (* \) matching the appropriate letters, this is the key point of this question.

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        # dynamic programming
        len_s = len(s)
        len_p = len(p)
        # initialize
        dp = [[False for i in range (len_p+1)] for j in range(len_s+1)]
        dp[0][0] = True
        # initialize dp[0][j]
        for j in range(1,len_p):
            if p[j] == '*':
                dp[0][j+1] = dp [0][j-1]
        for i in range(1,len_s+1):
            for j in range(1,len_p+1):
                if s[i-1] == p[j-1] or p[j-1] == '.':
                    dp[i][j] = dp[i-1][j-1]
                elif p[j-1] == '*':
                    dp[i][j] = (dp[i-1][j-2] and (p[j-2] == s[i-1] or p[j-2] == '.')) or dp[i][j-2] or
                               (dp[i-1][j] and (s[i-1] == p[j-2] or p[j-2] =='.'))
        return dp[len_s][len_p]

There are other similar topic: [leetcode] 44.Wildcard Matching;

[leetcode]62.Unique Paths

The key is to get dynamic programming recurrence relations. For this problem, the number reaches a certain point of the path is equal to the number of paths that reach it and to its left and the number of paths. That is, since the total number of point (i, j) paths: ways [i] [j] = Number of starting point (i, j-1) is: ways [i] [j-1] + starting point ( i-1, j) Total: ways [i-1] [j]. So we get the Recursion: $ ways [i] [j] = ways [i] [j-1] + ways [i-1] [j] $

class Solution:
    def uniquePaths(self, m, n):
        paths = [[1 for i in range(n)] for i in range(m)]
        for i in range(1,m):
            for j in range(1,n):
                paths[i][j] = paths[i-1][j]+paths[i][j-1]
        return paths[m - 1][n - 1]

There are other similar topic: [leetcode] 63.Unique Paths II; [leetcode] 64.Minimum Path Sum

[leetcode]72.Edit Distance

Edit distance is also an extremely important and classic problem.
This algorithm is to s [1 ... i] is converted to t [1 ... j] (for example, converted to a sitting kitten) required minimum number (i.e. a so-called edit distance) operation, the operand is stored in d [i, j] (d is represented by the two-dimensional array shown above) in.

  • In the first row and first column is certainly correct, this is well understood, for example, we will kitten converted to an empty string, the number of operations we need for the operator of the length kitten (kitten will be carried out for all characters throw away).
  • We have three characters may be in operation:
    • If we can use the k operands s [1 ... i] is converted to t [1 ... j-1], we only need to t [J] can be added to the end face of the s [1 ... i] t is converted to [1 ... j], k + 1 operand
    • If we can use the k operands s [1 ... i-1] is converted to t [1 ... j], we only need to s [i] can be deleted from the last complete the conversion, operand k + 1
    • If we can use the k operands to s [1 ... i-1] is converted to t [1 ... j-1], we need only in case of need (s [i]! = T [j]) to s [i] is replaced with t [j], the required number of operations for the k + cost (cost representative of whether conversion, if s [i] == t [j], the cost is 0, otherwise 1).
  • The s [1 ... n] is converted to t [1 ... m] of course need to convert all s for all t, it is, d [n, m] (right of the table) is the result we need.
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m,n = len(word1),len(word2)
        dp = [[0 for j in range(n+1)] for i in range(m+1)]

        for i in range(m+1):
            for j in range(n+1):
                if i==j==0:
                    dp[0][0] = 0
                elif i==0:
                    dp[0][j] = j
                elif j==0:
                    dp[i][0] = i
                else:
                    if word1[i-1] == word2[j-1]:
                        dp[i][j] = dp[i-1][j-1]
                    else:
                        dp[i][j] = 1+min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])
        return dp[-1][-1]
[leetcode]91.Decode Ways

Obviously, since this question requires only decode how many ways there are, without requiring to each method are listed, so with DP. Suppose the current element is Z, two in front of him as XY, ieXYZ.
1. If Z = 0, and Y = 1 or 2. Then Z corresponds to a DP value of X is equal to DP, because only one explanation for the embodiment 10, the DP [i] = DP [i -2].
2. If located YZ [11, 19] or [21, 26] both in range, it is clear that we have two two-digit to decode this manner, i.e. DP [i] = DP [i -1 ] + DP [i-2] , Note that this is not the DP [i] = DP [i -1] +1. The last example is a 21,212.
3. If X is not 0, e.g. YZ = 81. Then DP [i] = DP [i -1].
Finally, note that because we have to take 2 to DP [i-2], so we initial DP list when adding a 1 in front. Because of this plus 1 foremost. So when DP [i] corresponds to s [i-1]. YZ is the corresponding s [(i-1) -1 : (i-1) +1].

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s or s[0] == '0':
            return 0
        dp = [1 for i in range(len(s)+1)]
        for i in range(2,len(s)+1):
            nums = s[i-2:i]
            if 1<int(nums)<10 :
                dp[i] = dp[i-1]
            elif 11<=int(nums)<=19 or 21<=int(nums)<=26:
                dp[i] = dp[i-1]+dp[i-2]
            elif int(nums) == 10 or int(nums) == 20:
                dp[i] = dp[i-2]
            elif nums[-1]=='0':
                dp[i] = 0
            else:
                dp[i] = dp[i-1]
        return dp[-1]
[leetcode]97.Interleaving String

It can be done recursively, in each of matched s1 or s2 to any one of the recursion. But time out.
So do consider using dynamic programming.
s1, s2 only two strings, it can be flattened into a two-dimensional map to determine whether the lower right corner from the top left corner went.
When reaching the i-th element s1, s2 reaches the j-th element:
the right step is on a map s2 [j-1] match s3 [i + j-1] .
The next step is to map s1 [i-1] match s3 [i + j-1] .

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        if not s1:return s2==s3
        if not s2:return s1==s3
        m,n = len(s1),len(s2)
        if m+n != len(s3):return False
        dp = [[False for j in range(n+1)] for i in range(m+1)]
        for i in range(m+1):
            for j in range(n+1):
                if i==j==0:
                    dp[i][j] = True
                elif i == 0:
                    dp[0][j] = (dp[0][j-1] and s2[j-1] == s3[j-1])
                elif j == 0:
                    dp[i][0] = (dp[i-1][0] and s1[i-1] == s3[i-1])
                else:
                    dp[i][j] = (dp[i-1][j] and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] and s2[j-1] == s3[i+j-1])
        return dp[m][n]
[leetcode]115.Distinct Subsequences

Mutually different sequences. Before the i-th and j-th line with the number before the T DP by S [i] [j] recorded, then the final goal is dp [S.size ()] [T.size ()];
initialization, j = 0 time, dp [i] [0] = 1, because all of them by deleting all become null characters, and there is only one.
The following recursive equation:
i and j are starting from 1, and j is not greater than i, because the length of at least one match starts, j is greater than i meaningless

  • If \ (i == j \) then dp [i] [j] = S.substr (0, i) == T.substr (0, j);
  • If \ (i! = J \) in two cases
    • ! S [i-1] = T [j-1], the influence is not added added i is the same, then dp [i] [j] = dp [i - 1] [j];
    • S [i-1] == [j-1] T time, then the current character match or mismatch Alternatively, the dp [i] [j] = dp [i - 1] [j -1] + dp [ i - 1] [j];
    class Solution:
      def numDistinct(self, s: str, t: str) -> int:
          m,n = len(s),len(t)
          dp = [[0 for j in range(m+1)] for i in range(n+1)]
          dp[0][0] = 1
          for  j in range(1,m+1):
              dp[0][j] = 1
          for i in range(1,n+1):
              for j in range(1,m+1):
                  if t[i-1] == s[j-1]:
                      dp[i][j] = dp[i-1][j-1]+dp[i][j-1]
                  else:
                      dp[i][j] = dp[i][j-1]
          return dp[n][m]
[leetcode]123.Best Time to Buy and Sell Stock III

This road is the best time to buy stocks of a series of problems in the most difficult and most complex one, in front of two Best Time to Buy and Sell Stock and Best Time to Buy and Sell Stock II ideas are very concise, very simple algorithm . This road is the most transactions requires twice, to find the maximum profit, or dynamic programming Dynamic Programming needs to solve, and here we need two recursive formula to update the two variables are local and global. In fact, we can find at least k times after a maximum of trading profits, find the general solution can be set k = 2, is the answer to this question. We define local [i] [j] is reaching up to the first day i j times last sale transaction and maximum profit in the last day to sell, this is a local optimum. Then we define global [i] [j] to arrive at the i-th day up to the maximum profit j transaction, this is the global optimum. They recursion formula:
local [I] [J] = max (Global [I -. 1] [J -. 1] + max (the diff, 0), local [I -. 1] [J] + the diff)
Global [ i] [j] = max ( local [i] [j], global [i - 1] [j])
where local optimum value is relatively small and the day before a transaction is greater than the difference between the globally optimal plus 0 , and the previous day plus a difference in local optimum value, whichever is greater, and the optimum global optimum compare local and global optimal previous day, as follows:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0
        # 二维数组
        # k = 2
        # local = [[0 for j in range(k+1)] for i in range(len(prices))]
        # glob = [[0 for j in range(k+1)] for i in range(len(prices))]      
        # for i in range(1,len(prices)):
        #     diff = prices[i]-prices[i-1]
        #     for j in range(1,k+1):
        #         local[i][j] = max(local[i-1][j]+diff,glob[i-1][j-1]+diff)
        #         glob[i][j] = max(glob[i-1][j],local[i][j])
        # return glob[-1][-1] 

        # 一维数组
        k = 2
        local = [0 for j in range(k+1)]
        glob = [0 for j in range(k+1)]
        for i in range(1,len(prices)):
            diff = prices[i]-prices[i-1]
            for j in reversed(range(1,k+1)):
                local[j] = max(local[j]+diff,glob[j-1]+diff)
                glob[j] = max(glob[j],local[j])
        return glob[-1]

In fact, it can be summarized as DP two states, representing the day have to sell and profit margins do not necessarily sell that day.

[leetcode]132.Palindrome Partitioning II

Enter a string, which was divided after dividing each substring must be "palindrome" structure, it requires a minimum number of division. Clearly, in order to strike a minimum number of division, a simple idea is exhaustive segmentation situation, and then find out after the split may constitute a palindrome substring segmentation method and the least number of times.
For a string, we need to consider all possible partitionings, this problem can be abstracted into a DP problem, for a string of length n, the set DP [i] [j] indicates whether the i-th character to the j-th character palindrome configuration, if yes, DP [i] [j] = 1; if not, DP [i] [j] = 0

class Solution:
    def minCut(self, s: str) -> int:
        n = len(s)
        dp = [(i-1) for i in range(n + 1)]
        for i in range(n + 1):
            for j in range(i):
                tmp = s[j:i]
                if tmp == tmp[::-1]:
                    dp[i] = min(dp[i], dp[j] + 1)
        return dp[n]
[leetcode]139.Word Break

Dynamic Programming. dp [i] represents the string s [: i] can be split into substrings meet the requirements. We can see that, if s [j: i] in the group given string, and dp [j] is True (i.e., string s [: j] can be split into substrings meet the requirements), then At this point dp [i] is also a True.

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp = [False for i in range(len(s)+1)]
        dp[0] = True
        for i in range(len(s)+1):
            for k in range(i):
                if dp[k] and s[k:i] in wordDict:
                    dp[i] = True
                    # break
        return dp[len(s)]

More on dynamic programming string, we can find: the optimal solution for the longest string of demand / short, maximum / small, we can solve the problem of decomposition by sub-problem analysis, done by solving the overall problem of child Optimal solution.

[leetcode]174.Dungeon Game

Bottom-up dynamic programming
in time to finish the last room of the remaining blood at least 1, so the final state can be used as an initial state, in order to move forward is determined by the number of blood must be at least at each location, so state is determined by a position below and a left side and a small state. Thus a basic state equation is: dp [i] [j] = min (dp [i + 1] [j], dp [i] [j + 1]) - dungeon [i] [j].

class Solution:
    def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
        m, n = len(dungeon), len(dungeon[0])
        dp = [[0 for j in range(n)]for i in range(m)]

        for i in reversed(range(m)):
            for j in reversed(range(n)):
                if i == m-1 and j == n-1:
                    dp[i][j] = max(0, -dungeon[i][j])
                elif i == m-1:
                    dp[i][j] = max(0, dp[i][j+1] - dungeon[i][j])
                elif j == n-1:
                    dp[i][j] = max(0, dp[i+1][j] - dungeon[i][j])
                else:
                    dp[i][j] = max(0, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])
                
        return dp[0][0] + 1
[leetcode]198.House Robber

Dynamic Programming DP. In essence an array corresponding to remove one or more non-adjacent number, and the maximum allowed.

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:return 0
        dp = [0 for i in range(len(nums)+1)]
        for i in range(1,len(nums)+1):
            if i==1:
                dp[1] = nums[0]
            else:
                dp[i] = max(dp[i-1],dp[i-2]+nums[i-1])
        return dp[-1]
[leetcode]213.House Robber II

The problem of multi-loop conditions, under this constraint would not steal more than a month while the first and last on it. So, to steal two cases: the first is not to steal the last room, the first room not steal second, seeking to steal these two methods can get the maximum value.

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:return 0
        elif len(nums)==1:return nums[0]
        elif len(nums)==2:return max(nums[0],nums[1])
        else:
            return max(self._rob(nums[1:]),self._rob(nums[:-1]))
        
    
    def _rob(self,nums):
        if not nums:return 0
        dp = [0 for i in range(len(nums)+1)]
        dp[1] = nums[0]
        for i in range(2,len(nums)+1):
                dp[i] = max(dp[i-1],dp[i-2]+nums[i-1])
        return dp[-1]
[leetcode]221.Maximal Square

Dynamic programming, so that A [i] [j] is represented by the maximum side length of a square in (i, j) is the lower right corner, the time complexity is \ (O (M * N) \) , the spatial complexity is \ (O (M * N) \) .

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        if not matrix:return 0
        m,n = len(matrix),len(matrix[0])
        dp = [[0 for j in range(n)] for i in range(m)]
        res = 0
        for i in range(m):
            for j in range(n):
                if i == 0:
                    dp[i][j] = int(matrix[i][j])
                elif j == 0:
                    dp[i][j] = int(matrix[i][j])
                else:
                    if matrix[i][j] == '1':
                        dp[i][j] = min(dp[i-1][j- 1],dp[i-1][j],dp[i][j- 1])+1
                    else:
                        dp[i][j] = 0
                res = max(res,dp[i][j])
        return res*res

Note that dp [i] [j] to update conditions, matrix [i] [j] == '1', is updated to dp [i-1] [j- 1], dp [i-1] [j] , dp [i] [j- 1] is the minimum value +

[leetcode]309.Best Time to Buy and Sell Stock with Cooldown

Two
states: 1. In the first day i'll buy a stock of the remaining profit = (i-2) capable of remaining days of sales profits - the i-th day stock price.
2. The maximum profit in the first day i sell a stock's total profit = the first (i-1) + the rest of the day to buy stocks current stock price.
That information needs to maintain two states, a maximum profit is to buy the remaining shares obtained, a maximum profit is obtained after selling stock, they rely on each other's information.
Again further analysis of how to maintain a maximum profit.
For buy, the day depending on whether or not to buy a large than if bought after remaining profit before buying, i.e., the state transition equation is:
Buy [I] = max (Buy [-I. 1], Sell [I-2] - prices [i-1]);
for sale, the same day depending on whether or not to sell stock to sell the availability of greater profits, the state transition equation is:
Sell [i] = max (Sell [I- 1], buy [i-1 ] + prices [i-1]);
also be based on the meaning of problems, it maintains three state, easier to understand.

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # if not prices:
        #     return 0
        # N = len(prices)
        # buy = [0 for i in range(N+1)]
        # sell = [0 for i in range(N+1)]
        # buy[1] = -prices[0]
        # for i in range(2,N+1):
        #     buy[i] = max(sell[i-2]-prices[i-1],buy[i-1])
        #     sell[i] = max(sell[i-1],buy[i-1]+prices[i-1])
        # return sell[N]
        
        if not prices:
            return 0
        N = len(prices)
        rest = [0 for i in range(N)]
        buy = [0 for i in range(N)]
        sell = [0 for i in range(N)]
        buy[0] = -prices[0]
        for i in range(1,N):
            rest[i] = max(rest[i-1],sell[i-1])
            buy[i] = max(buy[i-1],rest[i-1]-prices[i])
            sell[i] = max(sell[i-1],buy[i-1]+prices[i])
        return sell[-1]
[leetcode]312.Burst Balloons

Dynamic programming
is provided DP [i] [j] is the maximum value of i to j can be obtained during this interval, the state transition equation of dp [i] [j] = max (i <k <j) (dp [i] [ k] + dp [k] [ j] + a [i] * a [k] * a [j])

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        nums = [1] + nums + [1]
        n = len(nums)
        dp = [[0 for j in range(n)] for i in range(n)]
        
        # dp[i][j]代表(i,j)区间内取得的最大值  -->关键在于理解dp内涵
        for left in reversed(range(n)):
            for right in range(left+1,n):
                for i in range(left + 1, right):
                    dp[left][right] = max(dp[left][right],nums[left] * nums[i] * nums[right] +dp[left][i] + dp[i][right])
        return dp[0][-1]
[leetcode]368.Largest Divisible Subset

Using a one-dimensional DP, which means that the requirements of the subject array, DP [i] is the meaning of the subject to meet the maximum position from 0 ~ i array. First traverse each number by i, j and then from back to front can find the nums [i] divisible numbers, so that if it is judged divisible time, and then determines dp [i] <dp [j ] + 1, i.e. for whether the ending index i becomes the longest length of the array. In the case of variable length, it needs to be updated DP [i], while using the parent [i] can be updated before the number i is divisible. Also statistics for the entire array longest sub-array length.
Know for the longest sub-array after each position, we will know for the longest interval within 0 ~ n meet an array of conditions title, and finally need to iterate again, being able to use parent children array of statistical output out . Because this is the biggest index mx_index n terms, so the output is the reverse order.

class Solution:
    def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
        if not nums: return []
        N = len(nums)
        nums.sort()
        dp = [1] * N #LDS
        parent = [0] * N
        mx = 1
        mx_index = -1
        for i in range(N):
            for j in reversed(range(i)):
                if nums[i] % nums[j] == 0 and dp[i] < dp[j] + 1:
                    dp[i] = dp[j] + 1
                    parent[i] = j
                    if dp[i] > mx:
                        mx = dp[i]
                        mx_index = i
        res = []
        for k in range(mx):
            res.append(nums[mx_index])
            mx_index = parent[mx_index]
        return res[::-1]

Here the problem-solving skills are, at the same time save the state in dynamic programming, and use the extra space to restore the site.

[leetcode]486.Predict the Winner

Dynamic programming
1. The problem is not a direct comparison of the players and take the value of the element, but to put the problems of the difference between the two players take element. This is clever, it is a critical step.
2. Find the recursive expression: max (the nums [BEG] - Partition (BEG +. 1, End), the nums [End] - Partition (BEG, End +. 1))
3. Expression by a recursive algorithm is a recursive structure relatively simple. But to construct a non-recursive algorithm larger difficulty. For non-recursive algorithm, first assigned an initial value dp, this is our first step in problem-solving. In this problem, we use a two dp array nums array to represent an arbitrary starting end position and the difference between the two results.
Initially, we only know the value on the diagonal. dp [i] [i] = nums [i]. It is well understood. Next, since it is seeking an arbitrary start and end, for two-dimensional array, it is certainly a double loop. By recursive expression dp known elements and dynamic programming, we can construct the results of our needs. Non-recursive way from small to large problem-solving process.

class Solution(object):
    def PredictTheWinner(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        n = len(nums)
        dp_f = [[0 for j in range(n)] for i in range(n)]
        dp_g = [[0 for j in range(n)] for i in range(n)]
        for i in reversed(range(n)):
            for j in range(i,n):
                if i==j:
                    dp_f[i][j] = nums[i]
                else:
                    dp_f[i][j] = max(nums[i]+dp_g[i+1][j],
                   nums[j]+dp_g[i][j-1])
                    dp_g[i][j] = min(dp_f[i+1][j],dp_f[i][j-1])       
        return dp_f[0][-1] >= sum(nums)/2
[leetcode]494.Target Sum

The only problem solving digital array and the target value is equal to the number of programs, each number may be positive or negative sign (plus minus integer equal to negative). Since the target and SUM (nums) is a fixed value, thus solving the original problem is transformed into the subsets and nums equal to sum (P) of the number of problem programs, may be converted into solving the knapsack problem.

class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        sum_ = sum(nums)
        target = (sum_+S)/2
        if target!=int(target):
            return 0
        if target>sum(nums):
            return 0
        target = int(target)
        dp = [0 for i in range(target+1)]
        dp[0] =1
        for num in nums:
            for i in reversed(range(num,target+1)):
                dp[i] += dp[i-num]
        return dp[-1]
[leetcode]516.Longest Palindromic Subsequence

Len len establish a row column array dp - dp [I] [j] denotes the sub-string string I ~ j subscript consisting of a length of the longest substring palindromic ~ Finally, we need to return the dp [0] [ len-1] value.
dp array update: First, the pointer i from the tail to the head traversal, the pointer j starts from a rear element has been traversed to the tail pointer i ~ start dp [i] [i] values are 1, if the current i and j is equal to the element pointed to described can be applied to the length of palindromic i ~ j of the substring, the update dp [i] [j] = dp [i + 1] [j-1] + 2; if the current element is not equal, then the two instructions a i, j referred palindromic sequence elements do not contribute to, the dp [i] [j] is from dp [i + 1] [j ] and dp [i] [j-1 ] select a larger value It can be.

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        if not s:
            return 0
        n = len(s)
        dp = [[0 for j in range(n)] for i in range(n)]
        for i in reversed(range(n)):
            for j in range(i,n):
                if i == j:dp[i][j] = 1
                elif s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1]+2
                else:
                    dp[i][j] = max(dp[i+1][j],dp[i][j-1])
        return dp[0][-1]
[leetcode]576.Out of Boundary Paths

This question gives us a two-dimensional array, somewhere to put a football, a time to move any further in the four directions, a total of N steps can be moved, he asked us to have a total of how many mobile method can football remove the border, because the result could be a huge number, so let us take more than a large number. So we know that for a large number if this result by recursive solution stack is easy to burst, so the best solution to consider using DP. Then we use a three-dimensional array of DP, where dp [k] [i] [j] represents total down k steps, out of the total number of paths from the boundary (i, j) position. Then we come recursive formula for dp [k] [i] [j], stepped out of the total number of walking path boundary k is equal to k-1 to go around the four locations of the step out of the total number and the path boundary, If you have a border around a location, then add 1 directly, or to find out the value dp array, so that the entire update down, we can draw a number for each position take any number of steps of the path out of bounds Finally, simply return dp [N] [i] [j] is the desired result.

class Solution:
    def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:
        dp = [[0] * n for _ in range(m)]
        for s in range(1, N + 1):
            curStatus = [[0] * n for _ in range(m)]
            for x in range(m):
                for y in range(n):
                    v1 = 1 if x == 0 else dp[x - 1][y]
                    v2 = 1 if x == m - 1 else dp[x + 1][y]
                    v3 = 1 if y == 0 else dp[x][y - 1]
                    v4 = 1 if y == n - 1 else dp[x][y + 1]
                    curStatus[x][y] = (v1 + v2 + v3 + v4) % (10**9 + 7)
            dp = curStatus
        return dp[i][j]

Knapsack problem

[leetcode]322.Coin Change

Using dynamic programming, a need to build an array of length dp amount + 1, which means that the number of denominations to be capable amount + 1 from 0 to the minimum required coin.

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        dp = [float('inf') for i in range(amount + 1)]
        dp[0] = 0
        for coin in coins:
            for i in range(coin, amount + 1):
                # if dp[i - coin] != float('inf'):
                dp[i] = min(dp[i], dp[i - coin] + 1)
        return dp[amount] if dp[amount]!= float('inf') else -1
[leetcode]474.Ones and Zeroes

This question is selected as much as possible from the set of character strings and string 0 and 1 to ensure that the number does not exceed a predetermined value, for the difficulty of the subject Medium.
Title and 0-1 knapsack problem is similar, the difference here is to limit the number 0 and 1, while limiting the total weight of the 0-1 knapsack problem, dynamic programming is regarded as the classic title.
Where i represents the number of strings in the string before the number is not more than 0 j, 1 k up to no more than the number selected by dp [i] [j] [ k]. Statistics i-th string number 0 and 1 respectively and cnt0 cnt1, if the i-th string is taken dp [i] [j] [ k] = dp [i-1] [j-cnt0] [k -cnt1] + 1, if not take the i-th string is dp [i] [j] [ k] = dp [i-1] [j] [k], whichever is larger as the two DP [i] [ value j] [k] is. Since dp [i] [j] [ k] dp [i-1] [*] [*] is only relevant, where it can be reused \ (m * n \) data reduced spatial complexity \ (O (n-m *) \) , just from back to front can be traversed when traversing. Specific code:

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp = [[0 for j in range(n+1)] for i in range(m+1)]
        for str in strs:
            cnt0 = str.count('0')
            cnt1 = str.count('1')
            for i in reversed(range(cnt0,m+1)):
                for j in reversed(range(cnt1,n+1)):
                    dp[i][j] = max(dp[i][j],dp[i-cnt0][j-cnt1]+1)
        return dp[-1][-1]
[leetcode]518.Coin Change 2

Establish dp array, save a few steps to reach the current amount. One by the amount traversal, only to see before i can reach a sum of the number of steps j how much to achieve this is adding up dp [current amount - the amount of the i-th], and finally return to dp [amount].

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0 for i in range(amount+1)]
        dp[0] = 1
        for coin in coins:
            for i in range(coin,amount+1):
                dp[i] += dp[i-coin]
        return dp[-1]

to sum up

For the common dynamic programming problem, the following examples:
1. Fibonacci number (Climbing Stairs)
2.01 knapsack problem
3. The longest common subsequence
4. Traveling Salesman Problem n!

Attention:
there is a dynamic programming algorithm used in many routines title
scroll array, compressed state, (l Victoria, monotonic, quadrangle inequality (advanced routines))
nature: first, violence, looking for redundancy, redundancy elimination

Guess you like

Origin www.cnblogs.com/hellojamest/p/11697744.html