蓝桥杯动态规划基础篇(一)
一、什么是动态规划?有套路吗?
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。20世纪50年代初,美国数学家贝尔曼(R.Bellman)等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。动态规划的应用极其广泛,包括工程技术、经济、工业生产、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果。
用简单通俗的话来说:对于一个原问题,我们把这一个原问题拆分成多个子问题,直到子问题可以直接解决,再把子问题答案保存起来,以减少重复计算,使用子问题答案反推,得出原问题解。
动态规划的实现步骤:
- 确定是否为动态规划问题:是否可以把这个问题分解成可以求解的子问题
- 找关系:分解后的子问题之间有什么联系?确定原问题的子问题数量也很重要
- 表达递归关系:不要着急写代码,在写代码之前清楚地表达递归关系可以加强你对问题的理解并使过程高效
- 决定解决问题的迭代或递归方法:递归更好
- 添加记忆:记忆是存储子问题的结果并在需要解决类似的子问题时再次调用它们的过程。这将降低问题的时间复杂度。如果我们不使用记忆,类似的子问题会重复解决,这可能导致指数时间复杂度。
- 实现
二、动态规划的特征
重要的是要知道何时应用动态规划算法,让我们了解动态问题的 2 个特征
- 最优子结构:如果一个问题的最优解包含子问题的最优解,则该问题是最优子结构。只有满足最优子结构,才能用动态规划的方法求解。如果问题具有最优子结构,我们可以递归求最优解。此外,如果问题没有最佳解决方案,则没有定义递归算法的基础,也是不能使用动态规划方法求解。
- 重叠子问题:如果递归算法重复访问相同的子问题,则该问题称为重叠子问题。如果任何问题都有重叠的子问题(公共子子问题),那么我们可以通过只计算一次来改进子问题的重复实现。如果问题没有重叠的子问题,那么使用动态规划算法来解决问题是不可行的。
画个图说明:
三、动态规划三元素
正如上面所了解的,动态规划是将问题划分为各种子问题以找出最佳解决方案的算法。在使用动态规划方法解决问题陈述时,将问题总共分为3个元素以获得最终结果。这些元素是:
- 子结构: 子结构是将给定的问题陈述划分为更小的子问题的过程。在这里,我们设法根据子问题的解决方案来确定原始问题的解决方案。
- 表结构:子问题的解决方案需要解决后存储到一个表中。这很重要,因为我们知道,动态编程会多次重用子问题的解决方案,这样我们就不必一次又一次地重复解决同一个问题。
- 自下而上的方法:使用表格组合子问题的解决方案以达到最终结果的过程。该过程从解决最小的子问题开始,然后将它们的解决方案与不断增加的子问题结合起来,直到获得原始问题的最终解决方案。
四、生活中的动态规划
想象一下,你想去几条街外的一家新杂货店,但你不知道怎么去那里,这是你就有多条测试的路线,要解决通往商店的路线的子问题,您可以在智能手机的地图上查找路线,然后前往那里。如果您以递归方式思考,那么每次您想去商店时,你都必须花时间再次查找路线。取而代之的是,我们自然而然地动态思考,从我们第一次查找商店时就记住了去往商店的方向,因此我们以后去的时候就不需要花时间去查找它们了。
五、斐波那契数列
5.1 问题
斐波那契数基本规律:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
斐波那契数列 Fn 由递归关系定义:
F n = F n-1 + F n-2
在python中递归基本方法:
def r_fibo(n): if n <= 1: return n else: return(r_fibo(n-1) + r_fibo(n-2))
在这里,程序将一次又一次地调用自己来计算更多的值。基于递归的方法的时间复杂度的计算在 O(2^N) 左右。这种方法的空间复杂度是 O(N),因为递归可以最大到 N。
例如:给定一个数字 n,打印第 n 个斐波那契数。
输入输出例子:
输入:n = 2
输出:1输入:n = 9
输出:34
5.2 递归解决
思考:该数列的基本规律是求的那个数是左边两个数之和,可以总是看成两个数之和,我们在这里设置n表示第几个数,设置start表示第一个是,last表示第二个数。
实现如下:
#0,1,1,2,3,5.....
def fib(n, start, last):# 如果是第一个数,直接返回if n-1 == 0:return start# 如果n大于1else:new_last = start + last #第二个数向右移动一个,j就要前两个数相加start = last # 第二个数就变成第一个数return fib(n-1, start, new_last) # 继续调用if __name__ == "__main__":#开始两个数设置为0和1print(fibonacci(10, 0, 1))
现在调用了 n 次递归函数。例如计算第 5 项的斐波那契数列,递归的原始树:
fib(5) / \ fib(4) fib(3) / \ / \ fib(3) fib(2) fib(2) fib(1) / \ / \ / \ fib(2) fib(1) fib( 1) fib(0) fib(1) fib(0) / \
fib(1) fib(0)
递归如下:
fib(5) fib(4)fib(3)fib(2)fib(1)
如果用C语言实现也很简答,思路一样:
// 0,1,1,2,3,5,.....
#include<stdio.h>
int fib(int n)
{if (n <= 1)return n;
return fib(n-1) + fib(n-2);
}int main ()
{int n = 9;
printf("%d", fib(n));
return 0;
}
5.3 动态规划
我们可以通过存储到目前为止计算的斐波那契数来避免递归方法中的重复工作。
python版:这里我们主要是使用列表来存储值
# 0.1.1.2.3.5def fib(n):# 初始两个数,列表用来存储f = [0, 1]# range包左不包右for i in range(2, n+1):f.append(f[i-1] + f[i-2]) # 添加前两个胡的和return f[n]print(fib(9))
C语言实现:
// 0,1,1,2,3,5,.....#include<stdio.h>
int fib(int n)
{// C语言中的数组就是我们python中的列表,等效 ,用它来存储结果
int f[n+2]; // 0和1已经占了两个位置,我们计算后面的位置
int i;// 数组前两个值固定为0,1
f[0] = 0;
f[1] = 1;for (i = 2; i <= n; i++)
{// 例如:f[2]=f[1]+f[0]f[i] = f[i-1] + f[i-2];
}return f[n]; //索引返回第n个值
}int main ()
{int n = 9;
printf("%d", fib(n));
return 0;
}
六、加泰罗尼亚数列
6.1 问题
加泰罗尼亚数规律:1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862…
加泰罗尼亚数满足以下递归公式:
问题:求前n项的加泰罗尼亚数
6.2 归实现
直接套公式,前提你能看得懂这公式,它就是最基本的累加公式,大学生肯定看得懂吧?
python实现如下:
def catalan(n):# 只有一个情况if n <= 1:return 1# 根据公式:累加求和# 递归:catalan(i)*catalan(n-i-1)res = 0for i in range(n):res += catalan(i) * catalan(n-i-1)return resn=int(input('请输入n的值:'))
for i in range(n):print (catalan(i),end=" ")
6.3 动态规划
上面的递归实现做了很多重复的工作,由于存在重叠的子问题,我们可以为此使用动态规划。
实现如下:
def catalan(n):# 前两个值情况if (n == 0 or n == 1):return 1# 存储子问题结果的表,开始为全0catalan =[0]*(n+1)# 初始化前两个值catalan[0] = 1catalan[1] = 1# 把值覆盖进去for i in range(2, n + 1):for j in range(i):catalan[i] += catalan[j]* catalan[i-j-1]# 返回值return catalan[n]n=int(input('请输入n:'))for i in range(n):print(catalan(i), end=" ")
七、二项式系数
基本定义这是你在大学应该学过的,基本定义:
二项式系数的对称特性:
帕斯卡法则是一重要的递归等式:
基础问题我是不应该提到的,但是便于讲解,还是拿出来,因为这应该是大学生必备知识、
7.1 计算二项式的数学方法
递归公式:
其中特别指定:
阶乘公式,二项式系数最简洁的表达式是阶乘:
7.2 问题
接受两个参数 n 和 k,并返回二项式系数 C(n, k) 二项式系数值。例如,对于 n = 4 和 k = 2,函数应该返回 6,对于 n = 5 和 k = 2 ,它应该返回10。
7.3 递归求解
根据数学知识(上面说到的计算二项式中递归公式),递归为:
C(n, k) = C(n-1, k-1) + C(n-1, k)C(n, 0) = C(n, n) = 1
使用python实现如下:
def er(n, k):if k > n:return 0if k == 0 or k == n:return 1# 继续调用return er(n-1, k-1) + er(n-1, k)n = int(input('请输入n:'))
k = int(input('请输入k:'))
print ("C(%d,%d) = %d" % (n, k,er(n, k)))
运行例子:
请输入n:5
请输入k:2
C(5,2) = 10
C语言可以实现如下:
#include <stdio.h>int er(int n, int k)
{// 基本情况 if (k > n)return 0;if (k == 0 || k == n)return 1;// 回调 return er(n - 1, k - 1)+ er(n - 1, k);
}// 主函数调用
int main()
{int n, k ;printf("请输入n值:");scanf("%d",&n);printf("请输入k值:");scanf("%d",&k);printf("C(%d, %d) = %d ", n, k,er(n, k));return 0;
}
演示:
7.3 动态规划求解
python实现如下:
def er(n, k):#初始化为0,把值存在列表C = [[0 for x in range(k+1)] for x in range(n+1)]
# print(C)for i in range(n+1):for j in range(min(i, k)+1):# 基本情况if j == 0 or j == i:C[i][j] = 1# 调用else:C[i][j] = C[i-1][j-1] + C[i-1][j]# 返回n,k情况return C[n][k]# 5,2就是五行三列
# n = 5
# k = 2
n=int(input('请输入n:'))
k=int(input('请输入k:'))
# print(er(n,k))
print(" C[" + str(n) + "][" + str(k) + "] = "+ str(er(n, k)))
C语言实现如下:
#include <stdio.h>// 最小值函数
int min(int a, int b) { return (a < b) ? a : b; }//返回二项式系数 C(n, k) 的值
int er(int n, int k)
{int C[n + 1][k + 1];int i, j;// 计算二项式系数的值以自下而上的方式for (i = 0; i <= n; i++) {for (j = 0; j <= min(i, k); j++) {// 基本情况 if (j == 0 || j == i)C[i][j] = 1;// 存储值到数组 elseC[i][j] = C[i - 1][j - 1] + C[i - 1][j];}}// 返回值 return C[n][k];
}// 主函数
int main()
{int n , k ;printf("请输入n:"); scanf("%d",&n);printf("请输入k:"); scanf("%d",&k);printf("C(%d, %d) = %d ", n, k,er(n, k));return 0;
}
演示:
七、资料
可以到我的github仓库:计算机学习体系
如果你没有基础可看基础部分,数据结构没有基础看数据结构部分,如果你想直接做蓝桥杯真题,VIP题,蓝桥杯宝典,可以直接看蓝桥杯部分…应有尽有。
基础视频: b站
八、总结
你是否发现上面案例有相同之处?是否能总结出一个这种题型的代码模板?是否有固定的思考方式?请把你的思考记录下来,欢迎评论区回答你的思考,以便于大家交流。
加我vx拉你到蓝桥杯交流群:hxgsrubxjogxeeag
蓝桥杯动态规划基础篇(一)相关推荐
- java 龟兔赛跑预测_Java实现 蓝桥杯VIP 基础练习 龟兔赛跑预测
题目描述 话说这个世界上有各种各样的兔子和乌龟,但是 研究发现,所有的兔子和乌龟都有一个共同的特点--喜欢赛跑.于是世界上各个角落都不断在发生着乌龟和兔子的比赛,小华对此很感兴趣,于是决定研究不同兔 ...
- 蓝桥杯试题 基础练习 BASIC-11 十六进制转十进制 JAVA——冲刺蓝桥杯第六天
目录 前言 试题 基础练习 十六进制转十进制 要点 思路一:常规做法--进制转换 字符 循环 本题代码 思路二:简单做法,使用java自带的方法 本题代码 前言 进制转化包括这篇我写了三篇了,这篇没讲 ...
- 蓝桥杯: 基础练习 数列排序
蓝桥杯: 基础练习 数列排序 问题描述 给定一个长度为n的数列,将这个数列按从小到大的顺序排列.1<=n<=200 输入格式 第一行为一个整数n. 第二行包含n个整数,为待排序的数,每个 ...
- 【蓝桥杯】基础练习 特殊回文数
[蓝桥杯]基础练习 特殊回文数 试题 基础练习 特殊回文数 资源限制 时间限制:1.0s 内存限制:512.0MB 问题描述 123321是一个非常特殊的数,它从左边读和从右边读是一样的. 输入一 ...
- Python 蓝桥杯试题 基础练习 特殊回文数
Python 蓝桥杯试题 基础练习 特殊回文数 问题描述: 123321是一个非常特殊的数,它从左边读和从右边读是一样的. 输入一个正整数n, 编程求所有这样的五位和六位十进制数,满足各位数字之和等于 ...
- Python 蓝桥杯试题 基础练习 数列排序
Python 蓝桥杯试题 基础练习 数列排序 题目描述: 问题描述 给定一个长度为n的数列,将这个数列按从小到大的顺序排列.1<=n<=200 输入格式 第一行为一个整数n. 第二行包含n ...
- 蓝桥杯试题 基础练习 数列特征python
蓝桥杯试题 基础练习 数列特征python 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 给出n个数,找出这n个数的最大值,最小值,和. 输入格式 第一行为整数n,表示数的个数. ...
- 蓝桥杯试题 基础练习 Sine之舞
蓝桥杯试题 基础练习 Sine之舞 资源限制 时间限制:1.0s 内存限制:512.0MB 问题描述 最近FJ为他的奶牛们开设了数学分析课,FJ知道若要学好这门课,必须有一个好的三角函数基本功.所以他 ...
- 蓝桥杯STC基础代码-以定时器计数作为时间轴的模板
蓝桥杯STC基础代码 以定时器计数作为时间轴的模板 简介 配置过程 定时器的配置 定时器中断的配置 主函数的配置 代码下载地址 下一章 以定时器计数作为时间轴的模板 简介 这是我写STC15代码时候首 ...
最新文章
- centos7通过yum安装php方法
- jq js json 转字符串_JQuery如何把JSON字符串转为JSON对象
- 使用Exchange 的SMTP连接器路由/中继邮件的设定图例
- Docker系列之烹饪披萨(二)
- Programming Assignment 5: Burrows–Wheeler Data Compression
- plupload怎么设置属性_腾达无线路由器怎么设置,这些是你要知道的
- mysql的学习要点_MySQL中的联合索引的学习要点总结
- 最长回文子串_【每日编程142期】最长回文子串II
- ASP.NET实现文件上传和下载
- 2020-12-06 高等数学:常用积分公式
- 基于华为eNSP的企业网络规划
- 【数字孪生】UE4虚幻引擎WebUI插件,UE4.26中使用WebUI加载HTML的video无法显示的问题
- Office Word 2016 Mathtype出现omml2mml.xsl 问题的解决方法
- Dell灵越 5559笔记本安装固态硬盘 BIOS设置
- 阿尔茨海默病最新研究进展(2021年)
- OpenJ_Bailian - 4104 G - 单词翻转
- 企业级项目实战讲解!我凭借这份PDF的复习思路,薪资翻倍
- -bash:........ Permission denied
- linux 模拟硬盘故障,linux 硬盘故障解决
- java里false是什么意思_为什么 Java 中“1000==1000”为false,而”100==100“为true?
热门文章
- a按钮居中显示 bootstrap_Bootstrap提示冒泡样式
- HM4048E(带平衡功能、5V USB输入、8.4V/1.5A两节锂电池充电管理IC)
- Android学习-组件自动绑定
- [Linux] Linux 客户端NFS挂载后目录下中文名文件乱码
- JS选择日期控件。当前日期以后的日期不能选择
- android 字体倒影,Android开发中怎么实现一个文字倒影效果
- linux X配置文件,Linux和Windows互传文件 用户配置文件和密码配置文件 用户组管理 用户管理...
- MySQL有四种BLOB类型
- 【Codeforces 1349A】Orac and LCM
- c语言用单链表实现lru算法,手写单链表实现和LRU算法模拟