动态规划入门题目

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

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

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

  • 递推公式 = 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循环从小到大逐步求出所有台阶对应的方案数,最后返回dpn-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. 最左侧的节点:前驱节点只能是trii-10
  2. 最右侧的节点:前驱节点只能是trii-1i-1
  3. 中间的节点 :前驱节点可以是trii-1j or trii-1j+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())

算法优化:

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

记忆化搜索的复杂度

时间复杂度:

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(money0, money1)
  • 第三家店铺:最大金额等于 max(money1, money0+money2)
  • 第i(i > 3)家店铺:最大金额等于 moneyi-1 + max(dpi-2, dpi-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()])
相关推荐
懂点Tech3 小时前
ctfshow misc28:GIF 高度修改解题与脚本实现
开发语言·windows·python
comedate4 小时前
SMLib 的 Examples 导读:PyAPI / SM_API / SMLib 三组示例怎么读
python·cad·smlib·examples·sm_api
微尘hjx4 小时前
【ByteTrack 04】多轨迹算法ByteTrack改进点
算法·yolo·目标检测·目标跟踪·bytetrack·ultralytics·车跟踪
云和数据.ChenGuang4 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
摇滚侠4 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
小向是个Der4 小时前
嵌入式进阶——MCU上电后的动作
c语言·单片机·mcu·学习·链接脚本
川石课堂软件测试4 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
万化冥合5 小时前
PyBox屏幕键盘如何辅助演示键盘操作?
python·计算机外设
车软学堂5 小时前
从测试转开发,到底要补什么
网络·人工智能·python·功能测试·单片机·车载系统·自动化
天空依然蔚蓝5 小时前
分享一个自动检测网站在线状态的python程序
python·运维开发·1024程序员节