动态规划入门题目

动态规划(记忆化搜索):

将给定问题划分成若干子问题,直到子问题可以被直接解决。然后把子问题的答保存下来以免重复计算,然后根据子问题反推出原问题解的方法

动态规划也称为递推(暴力深搜+记忆中间状态结果)其中:

  • 递推公式 = dfs向下递归的公式
  • 递推列表的初始值 = 递归的边界

文章目录

一、爬楼梯

Problem One: 70. 爬楼梯

题目描述:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

输入:

输入一个整数表示台阶数 n

输出:

返回一个整数,表示爬到楼顶的方案数。

思路

  • 对第一级台阶,方案数等于1,对第二级台阶,方案数为2(直接迈两级或迈两次一级)
  • 对于第i级台阶(i >= 3)来说,它的前驱台阶可能是i-1,也可能是i-2,所以从第0阶台阶上到第i阶台阶的方案数等于上到第i-1阶台阶的方案数加上上到第i-2阶台阶的方案数。

解题方法

创建一个dp[]列表存储到达每一级台阶的方案数,使用for循环从小到大逐步求出所有台阶对应的方案数,最后返回dp[n-1]即可

记忆化搜索的复杂度

时间复杂度:

O ( n ) O(n) O(n)

空间复杂度:

O ( n ) O(n) O(n)

Python3 复制代码
class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 0:
            return 0
        elif n == 1:
            return 1
        dp = [0]*(n+1)
        dp[0], dp[1], dp[2] = 0, 1, 2
        for i in range(3, n+1):
            dp[i] = dp[i-1] + dp[i-2]
        return dp[-1]

根据 动态规划 = 记忆化搜索 的理念可知,本题可以直接使用暴力搜索完成,但算法的复杂度有明显差异。

传统搜索的复杂度

时间复杂度:

O ( 2 n ) O(2^n) O(2n)

空间复杂度:

O ( 1 ) O(1) O(1)

Python3 复制代码
# 使用DFS直接搜索
# 时间复杂度:O(2的n次方)
def f(n)->int:
    if n <= 0:
        return 0
    elif n == 1:
        return 1
    elif n == 2:
        return 2
    else :
        return f(n-1)+f(n-2)

print(f(int(input())))

二、三角形最小路径和

Problem Two: LCR 100. 三角形最小路径和

题目描述:

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

输入:

输入一个二维列表表示三角形

例如:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]

输出:

返回一个整数,表示最小路径和。

思路

  • 对第一级台阶,方案数等于1,对第二级台阶,方案数为2(直接迈两级或迈两次一级)
  • 对于第i级台阶(i >= 3)来说,它的前驱台阶可能是i-1,也可能是i-2,所以从第0阶台阶上到第i阶台阶的方案数等于上到第i-1阶台阶的方案数加上上到第i-2阶台阶的方案数。

思路

题目是一个标准的深度优先搜索问题,因为每一层中的元素会多次被使用,所以可以通过创建列表存储到达该点所需要的最小路径和(记忆化搜索)的方式提升效率。记忆化搜索也就是动态规划。

解题方法

自然的方法是自上而下,找出每一层各元素到三角形上顶点的最小路径和,然后在最后一层中选出最小路径。但这种方法中每一层的元素需要分成左端点、中间节点和右端点三类,最后还需要遍历一整行找出最小值,算法的时间复杂度较高。

  1. 最左侧的节点:前驱节点只能是tri[i-1][0]
  2. 最右侧的节点:前驱节点只能是tri[i-1][i-1]
  3. 中间的节点 :前驱节点可以是tri[i-1][j] or tri[i-1][j+1]

传统搜索的复杂度

时间复杂度:

O ( n 2 ) O(n^2) O(n2)

空间复杂度:

O ( n ) O(n) O(n)

python 复制代码
# 自上而下计算最小和
def f()->int:
    # 读取输入
    n = int(input())
    tri = []
    for i in range(n):
        tri.append([int(j) for j in input().split()])
    # 算法主体
    if not tri:
        # 如果列表为空,返回0
        return 0
    rows = len(tri)
    dp = []
    # 第一行特殊处理
    dp.append([tri[0][0]])
    for i in range(1, rows):
        temp = []
        for j in range(i+1):
            if j == 0:
                temp.append(tri[i][0]+dp[i-1][0])
            elif j < i:
                temp.append(tri[i][j]+min(dp[i-1][j-1], dp[i-1][j]))
            else:
                temp.append(tri[i][j]+dp[i-1][j-1])
        dp.append(temp)
    return min(dp[-1])
print(f())

算法优化:

通过对题目的观察可知题目不要求找出最短路径中的每一个元素,所以也可以自下而上的查找,存储每一个元素到最后一层的最小路径和。这样每一层只有一种节点(前驱节点一定是 tri[i+1][j] 和 tri[i+1][j+1] 二选一),不必区分左右端点,而且最后只需返回dp[0][0]即可,因为dp[0][0]存储的就是三角形上顶点到三角形最下层的最小路径和。

记忆化搜索的复杂度

时间复杂度:

O ( n 2 ) O(n^2) O(n2)

空间复杂度:

O ( n ) O(n) O(n)

python 复制代码
# 除了自上而下的计算,还可以自下而上地求出dp列表的值
# 在自下而上的过程中,三角形每一层都只有一种节点,更为简单
def f()->int:
    n = int(input())
    tri = []
    for i in range(n):
        tri.append([int(j) for j in input().split()])
    # 算法主体
    # 创建dp列表
    dp = tri[:]
    for row in range(n-2, -1, -1):
        for col in range(row+1):
            dp[row][col] = min(dp[row+1][col], dp[row+1][col+1]) + tri[row][col]
    return dp[0][0]

print(f())

三、大盗阿福

Problem Three: 信息学奥赛一本通 1301. 大盗阿福

题目描述:

阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。

这条街上一共有 N 家店铺,每家店中都有一些现金。阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。

作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?

输入:

输入的第一行是一个整数T(T≤50) ,表示一共有T组数据。

接下来的每组数据,第一行是一个整数N(1≤N≤100,000) ,表示一共有N家店铺。第二行是N个被空格分开的正整数,表示每一家店铺中的现金数量。每家店铺中的现金数量均不超过1000。

输出:

对于每组数据,输出一行。该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。

思路

分析可知,被选中的两家店之间可能间隔1家店,也可能间隔2家店,但不可能间隔大于等于3家店,因为间隔的三家店中中间的那一家也可以同时被选中,如果不选,则该方案一定不是最优方案。所以本题实质上与爬楼梯相似,都是典型的动态规划题目。

解题方法

创建一个列表dp记录从左向右打劫到每一个店铺时所能获得的最大金额,最后dp中的最大值即为所求。

  • 第一家店铺:最大金额等于该店铺的钱数
  • 第二家店铺:最大金额等于 max(money[0], money[1])
  • 第三家店铺:最大金额等于 max(money[1], money[0]+money[2])
  • 第i(i > 3)家店铺:最大金额等于 money[i-1] + max(dp[i-2], dp[i-3])

复杂度

时间复杂度:

O ( n ) O(n) O(n)

空间复杂度:

O ( n ) O(n) O(n)

python 复制代码
def f(n, money):
    # n = len(money)
    if n == 1:
        print(money[0])
        return 
    elif n == 2:
        print(max(money))
        return
    elif n == 3:
        print(max(money[1], money[0]+money[2]))
        return
    # 初始化dp列表
    dp = [0]*n
    dp[0] = money[0]
    dp[1] = max(money[0], money[1])
    dp[2] = max(money[1], money[0] + money[2])
    for i in range(3, n):
        dp[i] = money[i] + max(dp[i-2], dp[i-3])
    print(max(dp))

t = int(input())
for i in range(t):
    f(int(input()), [int(k) for k in input().split()])
相关推荐
火羽白麟1 分钟前
大坝安全的“大脑”——模型与算法
算法·模型·大坝安全
x70x803 分钟前
C++中auto的使用
开发语言·数据结构·c++·算法·深度优先
xu_yule8 分钟前
算法基础-单源最短路
c++·算法·单源最短路·bellman-ford算法·spfa算法
计算机毕设指导611 分钟前
基于微信小程序+django连锁火锅智慧餐饮管理系统【源码文末联系】
java·后端·python·mysql·微信小程序·小程序·django
colourmind11 分钟前
记录一次vscode debug conda python 使用报错问题排查
vscode·python·conda
Evand J13 分钟前
【MATLAB免费例程】多无人机,集群多角度打击目标,时间与角度约束下的协同攻击算法,附下载链接
算法·matlab·无人机
智航GIS15 分钟前
2.1 变量与数据类型
开发语言·python
旧梦吟16 分钟前
脚本工具 批量md转html
前端·python·html5
YGGP17 分钟前
【Golang】LeetCode 118. 杨辉三角
算法·leetcode
byzh_rc24 分钟前
[模式识别-从入门到入土] 无监督学习
学习·机器学习·支持向量机