• Dynamic Programming_Leetcode_部分动态规划题分析


    5. Longest Palindromic Substring

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

    Example 1:

    Input: "babad"
    Output: "bab"
    Note: "aba" is also a valid answer.
    

    Example 2:

    Input: "cbbd"
    Output: "bb"
    
    思路:
    该题中动态规划并不是最优解,但却是一个较容易理解的思路
    设dp[i][j]是一个布尔值,且其意味着字符串s第i位到第j位截取出来的片段是否为回文。
    即 if dp[i][j] == True: s[i:j+1] 为回文
    关键思路:
    如何判定dp[i][j]是True?
    当dp[i+1][j-1] == True 并且s[i] == s[j]时, dp[i][j] = True
    解释一下:
    为了知道s[i:j+1]是否是回文
    首先,s[i] 必须等于s[j],首尾必须呼应,这是规矩
    其次只要s[i+1:j+1-1]是回文,那两边同时加上一样的字符的s[i:j+1]也必定是回文.

    那么如何知道s[i+1:j+1-1]是回文?同样需要两个条件:s[i+1] == s[j-1] and s[i+1+1:j+1-1-1]
    那么如何知道s[i+2:j-1]是回文?....

    dp就可以开始规划了:

    class Solution(object):
        def longestPalindrome(self, s):
            """
            :type s: str
            :rtype: str
            """
            l, r = 0, 0
            dp = [[False for i in range(len(s))] for j in range(len(s))]#创造动态表
            for gap in range(len(s)):#跨度从最小的0开始判定
                for i in range(len(s)-gap):
                    j = i+gap
                    dp[i][j] = (j-i <=1 or dp[i+1][j-1]) and s[i] == s[j]#为了避免out of range, 长度小于等于2且首尾相等的字符串直接判定为True
                    if dp[i][j] and j-i+1 > r-l:#如果长度大于原记录,则把左右坐标替代
                        l,r = i, j
            return s[l:r+1]

    132. Palindrome Partitioning II
    Hard

    Given a string s, partition s such that every substring of the partition is a palindrome.

    Return the minimum cuts needed for a palindrome partitioning of s.

    Example:

    Input: "aab"
    Output: 1
    Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
    
    思路:
    设dp[i]为s[:i+1]字符串中为达成目标需要的最少切割数
    假设存在一个index_j,使 s[j:i+1]为回文
    则dp[i] = dp[j-1]+1 (j往前字符串所需要的切割数+1)

    由此:
    class Solution(object):
        def minCut(self, s):
            """
            :type s: str
            :rtype: int
            """
            dp = [float('inf') for i in range(len(s))]
            for i in range(len(dp)):
                for j in range(i+1):
                    if s[j:i+1] == s[j:i+1][::-1]:
                        if j == 0:#从起点出发则不需要切割
                            dp[i] = 0
                        else:
                            dp[i] = min(dp[i], dp[j-1]+1)
            return dp[-1]

    198. House Robber
    Easy

    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[i]为当决策是否抢i号房时的最大收益,
    根据规则,如果决定要抢i号房,则收益为nums[i] + dp[i-2]
    如果决定不抢,则收益为dp[i-1]
    由此dp[i] = max(dp[i-1],nums[i]+dp[i-2])

    class Solution(object):
        def rob(self, nums):
            """
            :type nums: List[int]
            :rtype: int
            """
    if not nums:
    return 0
    if len(nums) <= 2:
    return max(nums)
    dp = nums[:]
    dp[1] = max(dp[0], dp[1]) #由于抢第四间房时,前面的第一间与第二间都是可以作为选择的,而根据我们的思路dp[i] = nums[i]+dp[i-2] 取不到第一间
    #如果第一间房事实上大于第二间房,那就取不到正确的值了,所以设dp[1] 为两者中的最大值
    for i in range(2, len(nums)): #要注意是从第三间房开始
    dp[i] = max(nums[i]+dp[i-2], dp[i-1])
    return max(dp)

    213. House Robber II
    Medium

    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.
    

    Example 2:

    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.

    思路:
    类似于上一题,但是加了限制条件:房子摆成一个圈,意味着如果抢了第一个房子,最后一个房子便不能抢。
    所以把nums拆开两个部分讨论:nums[1:],nums[:-1]
    比较两种环境下的最大值,其他操作同上一题

    class Solution(object):
        def rob(self, nums):
            """
            :type nums: List[int]
            :rtype: int
            """
            if not nums:
                return 0
            if len(nums) <= 3:
                return max(nums)
            return max(self.helper(nums[1:]), self.helper(nums[:-1]))
        
        def helper(self, cut):
            cut[1] = max(cut[0], cut[1])
            for i in range(2, len(cut)):
                cut[i] = max(cut[i-1], cut[i-2]+cut[i])
            return max(cut)

    221. Maximal Square
    Medium

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.

    Example:

    Input: 
    
    1 0 1 0 0
    1 0 1 1 1
    1 1 1 1 1
    1 0 0 1 0
    
    Output: 4
    

    思路:
    设dp[i][j]为矩阵第i行第j列的正方形可达到的最大边长,则dp[i][j] = min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1。画个图会更好理解,这里来不及画先空着。
    直接在原matrix里替换值,注意matrix里的数字是string格式,所以要先替换
    class Solution(object):
        def maximalSquare(self, matrix):   
    for i in range(len(matrix)):
    for j in range(len(matrix[0])):
    matrix[i][j] = int(matrix[i][j])
    if matrix[i][j] and i and j: #如果matrix[i][j] == 0, 则跳过,因为组合不出正方形,同理如在边上,也跳过
    matrix[i][j] = int(min(matrix[i-1][j-1], matrix[i-1][j], matrix[i][j-1])) + 1
            return len(matrix) and max(map(max, matrix))**2 #储存的只是边,还要提出来2次方才是面积

    279. Perfect Squares
    Medium

    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实现更快的解法,我之前分析过:https://www.cnblogs.com/phinza/p/10269948.html
    这里讨论用动态规划解。

    思路:
    设dp[i]为i所需的最少完美平方数的组合数量
    根据n,可以求出可以使用的完美平方数的集为 lst = [u**2 for u in range(1,int(n**0.5)+1) if u**2 <= n]
    设num为lst中的任意一个完美平方数
    则dp[i] = dp[i-num]+1
    or dp[i] = 1 if i == num

    class Solution(object):
        def numSquares(self, n):
            lst = [i**2 for i in range(1,int(n**0.5)+1) if i**2 <= n] ##建立一个包含所有小于n的平方数列
            dp = list(range(n+1))
            for i in range(1,len(dp)):
                for num in lst:
                    if i-num >= 0:
                        dp[i] = min(dp[i], dp[i-num]+1)
                    else: #小于0则直接跳出,因为后面的数会越来越大,i-num必然小于0
                        break
            return dp[-1]

    300. Longest Increasing Subsequence
    Medium

    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. 

    思路:
    设dp[i]为第i个数字时的最大连续增加数的长度
    设在范围[0,i-1]中存在一个index_j,且nums[j] < nums[i]
    则dp[i] = dp[j]+1

    class Solution(object):
        def lengthOfLIS(self, nums):
    if not nums:
    return 0
    dp = [1] for i in range(len(nums)):
    for i in range(1, len(dp)):
    for j in range(i):
    if nums[j] < nums[i]:
    dp[i] = max(dp[i], dp[j]+1)
    return dp[-1]

    
    
    322. Coin Change
    Medium

    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
    
    思路:
    设dp[i]为合为i的组合中的最少硬币数量
    设j为coins中的某个数且i >= j
    则dp[i] = dp[i-j]+1

    class Solution(object):
        def coinChange(self, coins, amount):
            """
            :type coins: List[int]
            :type amount: int
            :rtype: int
            """
            dp = [0] + [float('inf') for i in range(amount)]#留个0位为0,这样当j==i时直接可以得到1.因为dp[0]+1 = 1,可省略一些步骤
            for i in range(1, len(dp)):
                for j in coins:
                    if j <= i:
                        dp[i] = min(dp[i], dp[i-j]+1)
            return dp[-1] if dp[-1] != float('inf') else -1#当目标数是无限大时,说明凑不出组合,则返回-1

    
    
    413. Arithmetic Slices
    Medium

    A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.

    For example, these are arithmetic sequence:

    1, 3, 5, 7, 9
    7, 7, 7, 7
    3, -1, -5, -9

    The following sequence is not arithmetic.

    1, 1, 2, 5, 7

    A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N.

    A slice (P, Q) of array A is called arithmetic if the sequence:
    A[P], A[p + 1], ..., A[Q - 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q.

    The function should return the number of arithmetic slices in the array A.


    Example:

    A = [1, 2, 3, 4]
    
    return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.
    
    思路:设DP[i]为第i个位置时的均差切片数量
    如果A[i]-A[i-1] == A[i-1]-A[i-2]
    则说明又有一个符合条件的均差切片产生
    但如果前面已有连续的一个切片,则应该+2
    例如[1,2,3,4],到4时:能产生[1,2,3],[2,3,4],[1,2,3,4]
    如果前面有连续的两个切片,则应该+3
    例如[1,2,3,4,5],到5时,能产生[1,2,3],[2,3,4],[1,2,3,4],[3,4,5],[2,3,4,5],[1,2,3,4,5]
    一旦连锁断掉,可加的值回到1:
    DP[i] = DP[i-1]+j

    class Solution(object):
        def numberOfArithmeticSlices(self, A):
            """
            :type A: List[int]
            :rtype: int
            """
            if not A:
                return 0
            temp = [0] * len(A)
            j = 1 #设跳跃点
            for i in range(2, len(A)):
                if A[i]-A[i-1] == A[i-1] - A[i-2]:
                    temp[i] = temp[i-1] + j
                    j += 1
                else:
                    temp[i] = temp[i-1]
                    j = 1
            return temp[-1]

    416. Partition Equal Subset Sum
    Medium

    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].
    

    Example 2:

    Input: [1, 2, 3, 5]
    
    Output: false
    
    Explanation: The array cannot be partitioned into equal sum subsets.
    

    思路:
    题目要求把所给list切割为两半以达到两边合相等
    其实就是切一刀使一边合为总合的一半
    那就是求sum(nums)//2的值是否可构成。

    设dp[i]为布尔值,其代表为可否构成i
    则dp[i] = True if dp[i-j] == True,j为所给的数字之一。
    class Solution(object):
        def canPartition(self, nums):
            """
            :type nums: List[int]
            :rtype: bool
            """
            if sum(nums)%2 == 1: #如果为基数,则不可能达成
                return False
            target = sum(nums)//2
            dp = [True] + [False]*target
            for num in nums:
                dp = [dp[i] or (i >= num and dp[i-num]) for i in range(len(dp))]
            return dp[-1]
               




  • 相关阅读:
    BZOJ1299 [LLH邀请赛]巧克力棒
    BZOJ1046 [HAOI2007]上升序列
    BZOJ1798 [Ahoi2009]Seq 维护序列seq
    BZOJ2045 双亲数
    BZOJ2301 [HAOI2011]Problem b
    BZOJ1021 [SHOI2008]Debt 循环的债务
    BZOJ2618 [Cqoi2006]凸多边形
    BZOJ1069 [SCOI2007]最大土地面积
    BZOJ1051 [HAOI2006]受欢迎的牛
    2017年09月23日普级组 环
  • 原文地址:https://www.cnblogs.com/phinza/p/10855055.html
Copyright © 2020-2023  润新知