动态规划Ⅰ

动态规划 Dynamic Programming

• 拆分(Divide): 将一个复杂问题拆分成一系列的简单子问题,每一次解决一个子
问题并将其结果存储起来。理想情况下用基于内存的数据结构。
• 查找(lookup):在下一次遇到相同的子问题的时候,直接查找之前计算过的结果
而不是重新计算。理想情况下,使用这种方法可以以适当的增大内存占用为代价节
省计算时间。
• 存贮子问题的答案以避免从新计算的技术叫做 memorization(记忆化)
• 分治法:把问题拆分成若干独立子问题,解决每个子问题之后合并子问题的结果作
为原始问题的结果。
• Top-Down
• 动态规划:将问题拆分成一些列重复的子问题以得到越来越大的子问题的答案
• Bottom
 
经典DP问题:
• 斐波那契数列(Fibonacci):: f(n) = f(n-1)+f(n-2),
where f(1)=1, f(2)=1
• 汉诺塔(Hanoi Tower)
• 子序列的最大和
 
例一:问题描述:给定n,找到不同的将n写成1,3,4相加的方法
def coin(n):
    dp = [0] * (n + 1)
    dp[0] = dp[1] = dp[2] = 1
    dp[3] = 2
    for i in range(4, n + 1):
        dp[i] = dp[i - 1] + dp[i - 3] + dp[i - 4]
    
    return dp[n]    

例二:入室抢劫

问题描述:

假设你是一个职业抢劫犯,你打算洗劫一个街道。每一个房子里有一
定数量的钱,限制你的唯一条件是相邻的房子的安保系统是相连的,
如果你抢劫相邻的房子那么安保系统就会惊动警察。
ž 给定一个非负整数的列表代表每个房子当中的钱,计算在不惊动警察
的情况下你可以抢劫到的最多的钱
def rob(nums):
    n = len(nums)
    dp = [ [0] * 2 for _ in range(n + 1)]
    for i in range(1, n + 1):
        dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]) # forget it
        dp[i][1] = nums[i - 1] + dp[i - 1][0]       # let's do it
    return max(dp[n][0], dp[n][1])  

优化空间到O(1)

def rob(nums):
    yes, no = 0, 0
    for i in nums: 
        no, yes = max(no, yes), i + no
    return max(no, yes)

例三:入室抢劫Ⅱ

进阶问题:
ž 该街道的所有房子是圆形排列的。也就是说第一家和最后一家也是邻
居。安保系统的设置同上问题
def rob(nums):
    def rob(nums):
        yes, no = 0, 0
        for i in nums: 
            no, yes = max(no, yes), i + no
        return max(no, yes)
    return max(rob(nums[len(nums) != 1:]), rob(nums[:-1]))

例四:组织聚会

ž 你的公司在组织一个公司的年会。你们公司的组织形式非常严格,如
:一个以主席为根的树状结构。现在这个聚会有一个限制:雇员和他
的直接领导不能同时受邀参加年会。你希望能够让更多的人来参与到
年会中。
进阶问题:
ž 假设这是一个募款会,组织方会ᨀ前知道每个人的捐助意向,所以如
何使收到的款最大。
 
例五:瓷砖问题
• 假定给定一块n*2的地板,瓷砖的大小为1*2。计算需要使用的瓷砖数量。瓷
砖可以水平放置:1*2。也可以竖直放置:2*1。
def minCostClimbingStairs2(cost):
    dp0, dp1, dp2 = 0, 0, 0
    for i in range(2, len(cost) + 1):
        dp2 = min(dp0 + cost[i - 2], dp1 + cost[i - 1])
        dp0, dp1 = dp1, dp2
    return dp2

另:

最小台阶问题
• 有一个楼梯,每次可以走1层或者2层,cost数组表示每一层所需
要花费的值。
• 你可以从第0节或者第1节台阶开始走,一旦你交了过路费,你可
以上一个台阶或者两个台阶。试计算登顶的最少花费
方法同例5
 
例六:解码方式
• 一段包含着A-Z的短信用一下方式进行编码:
• 'A' -> 1
• 'B' -> 2
• ...
• 'Z' -> 26
• 给定一段编码的短信,计算解码的方式
def numDecodings(s):
    if s=="" or s[0]=='0': return 0
    dp=[1,1]
    for i in range(2,len(s)+1):
        # if it is 0, then dp[i] = 0
        result = 0
        if 10 <=int(s[i-2:i]) <=26:
            result = dp[i-2]
        if s[i-1] != '0':
            result += dp[i-1]
        dp.append(result)
    return dp[len(s)]

例七:独特二叉树搜索路径  (卡特兰数

• 给定n,用1…n个数字来表达Binary Search Tree,问有多少种
表达方式
def numTress(n):
    if n <= 2:
        return n
    sol = [0] * (n + 1)
    sol[0] = sol[1] = 1
    for i in range(2, n + 1):
        for left in range (0, i):
            sol[i] += sol[left] * sol[i - 1 - left]
    
    return sol[n]  

例八:最大子序列乘积

• 找到array中的连续子序列,该子序列的乘积最大
解:需同时保存最大最小值
def maxProduct(nums):
    if len(nums) == 0:
        return 0
    maximum = minimum = result = nums[0]
    for i in range(1, len(nums)):
        maximum, minimum = max(maximum * nums[i], minimum * nums[i], nums[i]), \
                           min(maximum * nums[i], minimum * nums[i], nums[i])
        result = max(result, maximum)
    return result

 

 
posted @ 2020-05-05 09:20  oldby  阅读(124)  评论(0编辑  收藏  举报