当前位置: 首页 > news >正文

动态规划入门题目

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

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

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

  • 递推公式 = 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)

class Solution:def climbStairs(self, n: int) -> int:if n == 0:return 0elif n == 1:return 1dp = [0]*(n+1)dp[0], dp[1], dp[2] = 0, 1, 2for 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)

# 使用DFS直接搜索
# 时间复杂度:O(2的n次方)
def f(n)->int:if n <= 0:return 0elif n == 1:return 1elif n == 2:return 2else :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)

# 自上而下计算最小和
def f()->int:# 读取输入n = int(input())tri = []for i in range(n):tri.append([int(j) for j in input().split()])# 算法主体if not tri:# 如果列表为空,返回0return 0rows = 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)

# 除了自上而下的计算,还可以自下而上地求出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)

def f(n, money):# n = len(money)if n == 1:print(money[0])return elif n == 2:print(max(money))returnelif n == 3:print(max(money[1], money[0]+money[2]))return# 初始化dp列表dp = [0]*ndp[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()])
http://www.lryc.cn/news/290687.html

相关文章:

  • 探索云性能测试的各项功能有哪些?
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • Git安装,Git镜像,Git已安装但无法使用解决经验
  • Python与CAD系列高级篇(二十五)分类提取坐标到excel(补充圆半径、线长度、圆弧)
  • Linux安装Influxdb
  • Flutter CustomPainter 属性介绍与使用
  • 基于Javaweb开发的二手图书零售系统详细设计【附源码】
  • 【JaveWeb教程】(35)SpringBootWeb案例之《智能学习辅助系统》登录功能的详细实现步骤与代码示例(8)
  • 6.1 内存模式概述
  • Python中容器类型的数据
  • 虚拟机安装Centos8.5
  • ENVI下基于知识决策树提取地表覆盖信息
  • arco design table遇到的一些问题
  • Linux系统——文本三剑客
  • 代码随想录算法训练营Day38|动态规划理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
  • C++中的指针空值nullptr
  • 【Linux网络编程】网络编程套接字(1)
  • vite+ts+vue3打包的过程和错误
  • 80.双指针实现删除有序数组中的重复项 II(中等)-面试经典150题
  • 基于大数据的B站数据分析系统的设计与实现
  • 机器学习模型预测贷款审批
  • Linux实验记录:使用firewalld
  • Vue之初识Vue CLI 脚手架
  • [Tcpdump] 网络抓包工具使用教程
  • MongoDB常用命令
  • 强敌环伺:金融业信息安全威胁分析——整体态势
  • FreeRTOS简介
  • 51单片机点灯
  • sql注入之union联合注入
  • activiti解决实现ExecutionListener spring 自动注入@Autowired为null问题