• 动态规划系列之四梅花桩问题


    题目

    Redraiment是走梅花桩的高手。Redraiment总是起点不限,从前到后,往高的桩子走,但走的步数最多,不知道为什么?你能替Redraiment研究他最多走的步数吗?
    例如梅花桩的高度:

    2 5 1 5 4 5
    

    结果分析:
    6个点的高度各为 2 5 1 5 4 5
    如从第1格开始走,最多为3步, 2 4 5
    从第2格开始走,最多只有1步,5
    而从第3格开始走最多有3步,1 4 5
    从第5格开始走最多有2步,4 5

    所以这个结果是3

    梅花桩问题是最长升序数组问题。

    动态规划解题思路

    使用动态规划解题,需要题目符合动态规划的解决思路。动态规划解题思路是:

    1. 将大的问题拆解成小一点问题,小问题和大问题的解决思路是类似的
    2. 小问题之间的关系能完成大问题的最优解

    通常拆解问题的方法有两种,一种是二分法,把数组拆成两个。第二种是将将数组逐个递减,拆解成多个小数组。对于本体来说,第二种方式比较适合。

    小规模问题和大规模问题之间有某种联系,这种联系就是动态转移方程。通过小规模问题的解决,能够解决大规模问题,直到解决最终的问题。
    题目是要求数组 [2,5,1,5,4,5]中最长的升序数组。可以按照动态规划的思路,将大问题拆解成小问题。求6个元素的最长升序,可以拆解成求5个,同理求5个元素的,可以拆解成求4个。最后一直到1个。

    [2,5,1,5,4,5]
    [2,5,1,5,4]
    [2,5,1,5]
    [2,5,1]
    [2,5]  
    [2]  
    

    各个小问题之间有一定联系,通过找到小问题之间的联系,求出最优解。小问题之间的联系就是状态转移方程。

    建立数学模型

    求最多的步数,但不知道是从哪一步开始算最多。所以就创建一个数组dp,数组dp中存储从第一个梅花桩到当前梅花桩的步数。因为求的步数,最少也走一步,所以dp的初始值就是

    dp = [1,1,1,1,...1]
    

    状态转移方程

    当前梅花桩最多的步数,就是拿当前梅花桩逐个和前面的梅花桩比较,当前梅花桩高就比较是比当前小的梅花桩的最大值+1大 还是 该梅花桩本身的值大。通俗来说就是找出梅花桩数值小的。即:

    dp[j] + 1 大 还是 dp[i] 大
    

    下标为0时,dp数组

    2 5 1 5 4 5
    1 1 1 1 1 1

    下标为1时,dp数组。
    因为 5 比 2大,所有,dp[0] + 1 > dp[1],所以dp[1]=1+1=2

    2 5 1 5 4 5
    1 2 1 1 1 1

    下标为2时,dp数组:
    1 < 5, 1< 2,所以dp数组不变,dp[2] = 1

    2 5 1 5 4 5
    1 2 1 1 1 1

    下标为3时,dp数组:
    5 > 1,所以dp[3] = max(dp[2]+1,dp[3]) = max(2,1) = 2
    同时,5 > 2 ,所以dp[3] = max(2,dp[0]+1) = max(2,2) = 2。
    所以dp[3] = 2

    2 5 1 5 4 5
    1 2 1 2 1 1

    下标为4时,dp数组:
    4 > 1 ,dp[4] = max(dp[4],dp[2]+1) = max(1,2) = 2

    2 5 1 5 4 5
    1 2 1 2 2 1

    下标为5时,dp数组:
    5 > 4 ,dp[5] = max(dp[5],dp[4]+1) = max(1,3) = 3
    5 > 1 ,dp[5] = max(3,1) = 3
    5 > 2 ,dp[5] = max(3,1) = 3

    2 5 1 5 4 5
    1 2 1 2 2 3

    边界值

    边界值很清晰就是第一个梅花桩就一步,也就是dp[1,x,x,x,x,x]

    代码实现

    def GetResult(l):
        n=len(l) # 传入list的长度
        dp=[1]*n # dp[i]表示以第i个桩为结尾,最多走多少步,初始是1步
        
        for i in range(n):# i表示第几个桩
            for j in range(i):# j表示i前面的桩
                if l[i]>l[j]: #当第j个桩小于第i个桩的值时,第j个桩有dp[j]个上升的序列。
                    dp[i]=max(dp[i],dp[j]+1)#这时比较dp[j]+1和dp[i]大。因为可能会存在比dp[j]更大的序列,所以要找出最大的。
        
        #dp代表下标为i时上升序列。max找出其中最大的即为所求
        return max(dp)
     
    if __name__ == "__main__":
    
        l=[2,5,1,5,4,5]
        ans=GetResult(l)
        print(ans)
    

    小结

    梅花桩问题的的巧妙之处在于构建了一个dp数组,该数组中存储了到下标i最长升序个数。然后比较arr[i]和arr前面的所有元素的值,如果arr[i]大于前面的元素的值,那么也就是说arr[i]是升序的数组的下一个元素。但如果arr[i]大于很多个前面的元素的话,就要选择一个最大的值,所以循环比较i和之前的元素,取一个最大值。

    数组类动态规划解法

    最大和子数组最长升序数组 这两个问题能代表着数组类动态规划的求解方式,两个问题的相似之处为:
    a. 都将问题转化成以求解第i个元素为结尾的问题
    b. 构建dp数组,填充以第i个元素为结尾的最优解
    c. 都将大规模问题拆解成小规模问题
    d. 小规模问题小到极限有一个边界值,最大和的边界为只有一个元素时就是自身,最长升序的边界为只有一个元素时升序值为1

    这四个规律也是求解数组类动态规划问题的四个步骤

    同时两个问题也有不同之处:
    a. 最大和子数组,dp数组中代表着以i为结尾的数组的最大值,下一个值只和上一个值有关,保证连续性
    b. 最长升序数组,dp数组中代表这以i为结尾的升序数组的个数。下一个值与前面所有的值都有关联,要拿到前面值中的最大值。因为其不需要保证连续性,所有需要遍历前面的dp。最大和子数组只需要比较前一个。

  • 相关阅读:
    [9.运算符] 零基础学python,简单粗暴
    [8.转义字符&占位符] 零基础学python,简单粗暴
    [7.程序错误] 零基础学python,简单粗暴
    [6.注释与引号] 零基础学python,简单粗暴
    [5.数据类型] 零基础学python,简单粗暴
    国内的镜像网站大全
    mysql --好用又不贵的函数
    django--dos命令行项目创建未成功错误
    python-----拓展包下载地址
    python----列表转字典
  • 原文地址:https://www.cnblogs.com/goldsunshine/p/13221674.html
Copyright © 2020-2023  润新知