python-动态规划

题目1. 最长递增序列-动态规划(即,找出一个给定序列中最长的递增序列?)

# 动态规划
'''
    一般思路:
        1. 穷举法/暴力搜索;
        2. 记忆化搜索,剪枝
'''


# TODO 1. 暴力破解
def find_max_len1(nums, i):
    '''
        暴力破解:找出最长递增序列:
        L(i) 从i开始的子序列长度:
            L(0) -> L(1) -> L(2) -> L(3) -> L(4)  # 最坏
            L(1) -> L(2) -> L(3) -> L(4)
            L(2) -> L(3) -> L(4)
            L(3) -> L(4)
            L(4)
    :param nums:
    :param i: 当前从第i个开始找
    :return: 从i开始找的最大长度
    '''
    if i == len(nums) - 1:
        return 1

    max_len = 1
    for j in range(i + 1, len(nums)):
        if nums[i] < nums[j]:
            # 最大长度 = max(当前最大长度, 后续最大长度(find_max_len(nums, j))和自身(1))
            max_len = max(max_len, find_max_len1(nums, j) + 1)
    return max_len


def length_of1(nums):
    '''
        遍历数组,找出从锁有i开始后的递增序列长度
    :param nums:
    :return:
    '''
    return max([find_max_len1(nums, i) for i in range(len(nums))])


# TODO 2. 暴力破解+剪枝
def find_max_len2(nums, i):
    '''
        避免重复节点的计算:找出最长递增序列:
        动态规划就是通过避免重复节点的计算,加速计算的过程,用到字典或者哈希表保留计算的中间结果,也称为记忆化搜索。即空间换时间,带备忘录的递归,递归树的剪枝
    :param nums:
    :param i: 当前从第i个开始找
    :return: 从i开始找的最大长度
    '''
    if i in memo:
        return memo[i]

    if i == len(nums) - 1:
        return 1

    max_len = 1
    for j in range(i + 1, len(nums)):
        if nums[i] < nums[j]:
            # 最大长度 = max(当前最大长度, 后续最大长度(find_max_len(nums, j))和自身(1))
            max_len = max(max_len, find_max_len2(nums, j) + 1)
    memo[i] = max_len
    return max_len


def length_of2(nums):
    '''
        遍历数组,找出从锁有i开始后的递增序列长度
    :param nums:
    :return:
    '''
    return max([find_max_len2(nums, i) for i in range(len(nums))])


# TODO 3. 动态规划
def length_of3(nums):
    '''
        动态规划: 从后往前算(这里的L是find_max_len函数)
        L(0) = max(L(1), L(2), L(3), L(4)) + 1
        L(1) = max(L(2), L(3), L(4)) + 1
        L(2) = max(L(3), L(4)) + 1
        L(3) = max(L(4)) + 1
        L(4) = 1
    :param nums:
    :param i: 当前从第i个开始找
    :return: 从i开始找的最大长度
    '''
    n = len(nums)
    l = [1] * n
    for i in reversed(range(n - 1)):   # 3, 2, 1, 0
        for j in range(i + 1, n):
            # print(i, j)
            if nums[j] > nums[i]:
                l[i] = max(l[i], l[j] + 1)
            # print(l)
    return max(l)


if __name__ == '__main__':
    x = [1, 5, 2, 4, 3]
    memo = {}
    print(length_of1(x))
    print(length_of2(x))
    print(length_of3(x))

题目2. 青蛙跳(可以跳1 2 级),问有多少跳的方案?

# 青蛙跳
'''
    青蛙跳台阶:
        首先,当只有一级台阶时,毫无疑问,只有一种跳法。其次,当有两级台阶时,就是两种跳法
        那么,三级台阶时,应该两种情况
            1、若青蛙先跳一级台阶,接下来就有两种跳法,要么一级一级地跳,要么直接就跳上两级
            2. 若青蛙先跳两级台阶,接下来只能在再跳一级台阶(也可以先1后2)
        所以当有三级台阶时,一共有3种跳法
        那么,一共有4级台阶时,一共有多少种跳法呢?我们不妨列举一下
            1.青蛙先跳一级台阶,接下来他就会还有3级台阶要去跳,而这3级台阶不就是上面3级台阶的重复吗!所以此时一共有3种跳法
            2.青蛙先跳2级台阶,接下来他还有2级台阶要跳,此处也可以使用之前得出的2级台阶的结果,所以此时一共有2种跳法
        所以当青蛙要跳4级台阶时,其实就是跳3级台阶的跳法加上跳2级台阶的跳法
    总结:事实上,跳n级台阶的跳法就是跳n-1级台阶的跳法加上n跳-2级台阶的跳法,而这就可以使用递归的方法来解决
    ————————————————
    版权声明:本文为CSDN博主「fiance111」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/m0_60354608/article/details/123964767
'''


def jump_floor(n: int) -> int:
    '''
        递归做法
    :param n:
    :return:
    '''
    if n == 1:
        return 1
    elif n == 2:
        return 2
    else:
        return jump_floor(n - 1) + jump_floor(n - 2)


def jump_floor_v1(n: int) -> int:
    '''
        动态规划版本:
            eg: F(5) = F(4) + F(3)
                    F(4) = F(3) + F(2)
                    F(3) = F(2) + F(1)   # 发现F(2)重复计算;
                我们直接从后往前来
    :param n:
    :return:
    '''
    if n == 1:
        return 1
    elif n == 2:
        return 2
    dp = [1, 2]  # n = 1, n = 2
    for i in range(2, n):
        dp.append(dp[i - 1] + dp[i - 2])
    return dp[-1]


if __name__ == '__main__':
    print(jump_floor(3))
    print(jump_floor_v1(3))

 

posted @ 2022-09-01 16:07  落月_YU  阅读(661)  评论(0编辑  收藏  举报