美文网首页
算法(8):动态规划

算法(8):动态规划

作者: 大王叫我来巡老和山 | 来源:发表于2019-03-18 15:49 被阅读0次

    这是我最想写的一篇文章之一了~ 因为这玩意真难......



    例题1:求目标和(Target Sum,在算法(7)堆栈习题3中出现过,这里给出另一种解法)。给定一个数组以及一个目标数S,你可以给数组中每个数分配 运算符‘+’ 或 ‘-’ 其中之一,使得数组之和为目标S。输出共有多少种分配方式。
    输入: nums is [1, 1, 1, 1, 1], S is 3.
    输出: 5
    解释:
    -1+1+1+1+1 = 3
    +1-1+1+1+1 = 3
    +1+1-1+1+1 = 3
    +1+1+1-1+1 = 3
    +1+1+1+1-1 = 3
    代码解析:使用备忘录法。

    class Solution:
        def findTargetSumWays(self, nums: list, S: int, ) -> int:
            if nums == []:
                return 0
            dic = {nums[0]: 1, -nums[0]: 1} if nums[0] != 0 else {0: 2}
            for i in range(1, len(nums)):
                tdic = {}
                for d in dic:
                    tdic[d + nums[i]] = tdic.get(d + nums[i], 0) + dic.get(d, 0)
                    tdic[d - nums[i]] = tdic.get(d - nums[i], 0) + dic.get(d, 0)
                dic = tdic
            return dic.get(S, 0)
    
    if __name__ == '__main__':
        nums = [1, 1, 1, 1, 1]
        S = 3.
        solution = Solution()
        steps = solution.findTargetSumWays(nums,S)
        print(steps)
    

    例题2:最大子数组(Maximum Subarray),给出一个数组,找到一个连续的子数组(至少一个元素),该子数组有最大和,返回该最大和。
    输入: [-2,1,-3,4,-1,2,1,-5,4],
    输出: 6
    解释: [4,-1,2,1] 有最大和,为6.

    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            curSum = maxSum = A[0]
            for num in A[1:]:
                # 这句代码可以理解成:curSum = num if curSum < 0 else num + curSum
                curSum = max(num, curSum + num)
                maxSum = max(maxSum, curSum)
    
            return maxSum
    

    例题3:解码方法(Decode Ways)。
    给A到Z编码如下:

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

    现如今给一个非空字符串,里面由数字0到9组成,问有多少种解码方式?
    例题:
    输入: "226"
    输出: 3
    解释: "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

    注意事项:我当时做这道题的时候忽略了0这个特殊的存在,因为0不对应任何一个字母。比如一个字符串中出现了‘00’,那么该字符串输出就一定是0......下面给出了两种代码,供参考。

    class Solution:
        def numDecodings(self, s: str) -> int:
            # v, w, p = 0, int(s>''), ''
            # for d in s:
            #     v, w, p = w, (d>'0')*w + (9<int(p+d)<27)*v, d
            # return w
            # w tells the number of ways
            # v tells the previous number of ways
            # d is the current digit
            # p is the previous digit
        
        
            #dp[i] = dp[i-1] if s[i] != "0"
            #       +dp[i-2] if "09" < s[i-1:i+1] < "27"
            if s == "": return 0
            dp = [0 for x in range(len(s)+1)]
            dp[0] = 1
            for i in range(1, len(s)+1):
                if s[i-1] != "0":
                    dp[i] += dp[i-1]
                if i != 1 and "09" < s[i-2:i] < "27":  #"01"ways = 0
                    dp[i] += dp[i-2]
            return dp[len(s)]
    

    例题4:丑数,丑数为只能被2,3,5整除的数(1定义为第一个丑数),输入一个整数n,输出第n个丑数。
    输入:10
    输出:12(前十个丑数序列:1, 2, 3, 4, 5, 6, 8, 9, 10, 12)
    代码解释:用了三指针技术,对每个丑数再乘以2,3或5就是新的丑数,但是为了使得输出有序,需要三个指针来记录位置。

    class Solution:
        def nthUglyNumber(self, n: int) -> int:
            ugly = [1]
            i2, i3, i5 = 0, 0, 0
            while n > 1:
                u2, u3, u5 = 2 * ugly[i2], 3 * ugly[i3], 5 * ugly[i5]
                umin = min((u2, u3, u5))
                if umin == u2:
                    i2 += 1
                if umin == u3:
                    i3 += 1
                if umin == u5:
                    i5 += 1
                ugly.append(umin)
                n -= 1
            return ugly[-1]
    

    例题5:最长递增子序列(Longest Increasing Subsequence),给出一个未排序的整型数组,返回其最长递增子序列长度。
    输入: [10,9,2,5,3,7,101,18]
    输出: 4 (其中最长递增子序列为[2,3,7,101])
    代码解释:(下文sub 数组里存放的元素可能不是实际的递增子序列,但其长度是准确的)

    leetcode(Longest Increasing Subsequence)

    1. initial sub = [ ].
    2. traversing the nums:
      a) if val > sub's all elements, then subsequence length increased by 1, sub.append(val);
      b) if sub[i-1] < val < sub[i], then we find a smaller value, update sub[i] = val. Some of the elements stored in the sub[ ] are known subsequences, and the other part is elements of other possible new subsequences. However, the length of the known subsequences is unchanged.
    3. return the sub[ ]'s length.

    Here is the solution's track, as we have nums = [8, 2, 5, 1, 6, 7, 9, 3],when we traversing the nums:

    i = 0, sub = [8]
    i = 1, sub = [2]
    i = 2, sub = [2, 5]
    i = 3, sub = [1, 5], # element has been changed, but the sub's length has not changed.
    i = 4, sub = [1, 5, 6]
    i = 5, sub = [1, 5, 6, 7]
    i = 6, sub = [1, 5, 6, 7, 9]
    i = 7, sub = [1, 3, 6, 7, 9] #done! Although the elements are not correct, but the length is correct.

    Because of sub[ ] is incremental, we can use a binary search to find the correct insertion position.

    class Solution:
        def lengthOfLIS(self, nums: List[int]) -> int:
            # O(nlogn) solution with binary search
            def binarySearch(sub, val):
                lo, hi = 0, len(sub)-1
                while(lo <= hi):
                    mid = lo + (hi - lo)//2
                    if sub[mid] < val:
                        lo = mid + 1
                    elif val < sub[mid]:
                        hi = mid - 1
                    else:
                        return mid
                return lo
    
            sub = []
            for val in nums:
                pos = binarySearch(sub, val)
                if pos == len(sub):
                    sub.append(val)
                else:
                    sub[pos] = val
            return len(sub)
    

    我先把常见问题写在这,省的日后忘了:

    1. 求一个字符串的最长回文子串

    LeetCode 上的五种解法
    马拉车算法


    1. 两个字符串的最长子序列

    最长子序列

    1 2
    1. 两个字符串的最长公共子串

    最长公共子串

    公式比最长子序列更简单,如下所示:
    c[i][j]=1 +c[i-1][j-1] \ \ \ \ \ \ \ \ \ if \ \ x_i = y_i c[i][j] = 0 \ \ \ \ \ \ \ \ \ if \ \ x_i != y_i

    公共字串
    1. 背包问题

    背包九讲PDF下载链接:http://vdisk.weibo.com/s/zmfTPG2vgAcNV


    1. 股票买卖问题

    5种股票买卖问题
    上面的第五道题,买卖需要冷却的问题,可参考下面链接,讲的更详细,代码也更精致:Best Time to Buy and Sell Stock with Cooldown


    相关文章

      网友评论

          本文标题:算法(8):动态规划

          本文链接:https://www.haomeiwen.com/subject/fufbmqtx.html