蓝桥杯算法入门(Python) 课件 第8章动态规划_第1页
蓝桥杯算法入门(Python) 课件 第8章动态规划_第2页
蓝桥杯算法入门(Python) 课件 第8章动态规划_第3页
蓝桥杯算法入门(Python) 课件 第8章动态规划_第4页
蓝桥杯算法入门(Python) 课件 第8章动态规划_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

8.1动态规划概念蓝桥杯算法入门动态规划(DP)概述2算法竞赛有初级、中级、高级三个学习阶段。初级阶段:以搜索(DFS、BFS)为里程碑。中级阶段:以线段树为里程碑。高级阶段:以熟练应用高级动态规划为里程碑。DP:线性DP、DP应用(树形DP、状态压缩DP、数位统计DP等)、DP优化DP:地地道道的“计算思维”,独属于计算机学科的计算理论DP:非常适合用计算机编程实现。DP:一种需要学习才能获得的思维方法。算法竞赛:必考DP。蓝桥杯:任何一场比赛,都有1~3个DP题。动态规划难度知识点C组DP:普通一维问题[3-5]B组DP:背包DP[4-6]、树形DP[4-6]、状压DP[5-6]、数位DP[5-6]、DP的常见优化[7]“第十五届蓝桥杯大赛软件赛知识点大纲”中的动态规划动态规划DP是算法竞赛中最常见的考点之一,省赛、国赛都大量出现。2023年蓝桥杯省赛的DP题:C/C++:A组“更小的数”、B组“接龙数列”、C组“填充”、研究生组“奇怪的数”。Java:A组“高塔”、B组“数组分割,蜗牛,合并石子”、C组“填充”、研究生组“奇怪的数”。Python:A组“奇怪的数”、B组“松散子序列,保险箱,树上选点”、C组“填充,奇怪的数”、研究生组“填充,高塔”。DP概念动态规划动态规划(DP):将大问题分解为更简单的子问题,对整体问题的最优解决方案取决于子问题的最优解决方案。DP特征:重叠子问题:子问题是原大问题的小版本,计算步骤完全一样;其次,计算大问题的时候,需要多次重复计算小问题;最优子结构:大问题的最优解包含小问题的最优解;其次,可以通过小问题的最优解推导出大问题的最优解。动态规划动态规划与贪心:贪心不能得到最优解的题,DP往往可以(最少硬币问题)动态规划常用于:计数问题(求方案数)最值问题(最大价值、最小花费等)罗勇军8.2动态规划的两种编码方法蓝桥杯算法入门DP的两种编程方法DP的两种思路:自顶向下(Top-Down,先大问题再小问题)自下而上(Bottom-Up,先小问题再大问题)DP的两种编程方法:自顶向下:用带记忆化的递归编码自下而上:用递推编码两种方法的复杂度一样:每个子问题都计算一遍,而且只计算一遍。(1)自顶向下与记忆化自顶向下:先考虑大问题,再缩小到小问题,直到最小的问题无法再缩小。由于递归很直接地体现了这种思路,所以也可以用递归来写DP的代码。记忆化:为避免递归时重复计算子问题,可以在子问题得到解决时,就保存结果,再次需要这个结果时,直接返回保存的结果就行了。这种存储已经解决的子问题的结果的技术称为“记忆化”。11N=101dp=[0]*N#记忆结果deffib(n):ifn==1orn==2:return1ifdp[n]!=0:returndp[n]#已经计算过,直接返回结果,不再递归

dp[n]=fib(n-1)+fib(n-2)#递归计算,并记忆

returndp[n]print(fib(100))#第100个数是:354224848179261915075斐波那契数,记忆化代码:(2)自下而上与制表递推。这种方法与递归的自顶向下相反。思路:先解决子问题,再递推到大问题。通过填写表格来完成:根据表中的结果,逐步计算出大问题的解决方案。例:计算斐波那契数dp[1]dp[2]dp[3]dp[4]dp[5]dp[6]dp[7]dp[8]...1123581321...13N=101dp=[0]*Ndeffib(n):dp[1]=dp[2]=1foriinrange(3,n+1):dp[i]=dp[i-1]+dp[i-2]returndp[n]print(fib(100))斐波那契数,递推代码:罗勇军8.3

DP设计基础蓝桥杯算法入门DP的基本问题15状态设计状态转移编码实现更小的数LanqiaoOJ350316问题描述:有一个长度为n且仅由数字字符0~9组成的字符串,下标从0到n-1。你可以将其视作是一个具有n位的十进制数字num。小蓝可以从num中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字numnew满足条件numnew

<num。请你帮他计算下一共有多少种不同的子串选择方案。只要两个子串在num中的位置不完全相同我们就视作是不同的方案。注意,我们允许前导零的存在,即数字的最高位可以是0,这是合法的。输入:输入一行包含一个长度为n的字符串表示num(仅包含数字字符0∼9),从左至右下标依次为0∼n−1。输出:输出一个整数表示答案。1、DP状态设计17DP状态:定义二维数组dp[][],dp[i][j]表示子串s[i]~s[j]反转之后是否大于反转前的子串。dp[i][j]=1:表示反转之后变小,符合要求;dp[i][j]=0:表示反转之后没有变小。2、DP转移方程18对于每个子串,比较它的首尾字符s[i]和s[j],得到状态转移方程。(1)若s[i]>s[j],说明反转后的子串肯定小于原子串,符合要求,赋值dp[i][j]=1。(2)若s[i]<s[j],说明反转后的子串肯定大于原子串,赋值dp[i][j]=0。(3)若s[i]=s[j],需要继续比较s[i+1]和s[j-1],有dp[i][j]=dp[i+1][j-1]。3、编程实现19计算所有的dp[][]:长度为2的子串计算结果,在计算长度为4的子串时用到;长度为3的子串计算结果,在计算长度为5的子串时用到;…统计符合要求的dp[][]的数量,就是答案。20s=input()ans=0dp=[[0]*5010for_inrange(5010)]foriinrange(len(s)):

#子串从s[i]开始

forjinrange(i+1,len(s)):#子串末尾是s[j]ifs[i]>s[j]:dp[i][j]=1ifs[i]<s[j]:dp[i][j]=0ifs[i]==s[j]:dp[i][j]=dp[i+1][j-1]ifdp[i][j]==1:ans+=1print(ans)错误代码:问题出在for循环。例如第4行i=0,第5行j=8时,递推得dp[0][8]=dp[1][7],但是此时dp[1][7]还没计算。21根据DP的原理,应该先算出小规模问题的解,再递推大规模问题的解。计算应该这样进行:(1)初始化:dp[][]=0,其中的dp[0][0]=0、dp[1][1]=0、...、dp[1][0]、...,在后续计算中有用。(2)第一轮递推:计算长度为2的子串的dp[][],即计算出dp[0][1]、dp[1][2]、dp[2][3]、...。例如计算dp[0][1],若s0>s1,则dp[0][1]=1;若s0<s1,则dp[0][1]=0;若s0=s1,则dp[0][1]=dp[1][0]=0,这里dp[1][0]=0是初始化得到的。(3)第二轮递推:计算长度为3的子串的dp[][],即计算出dp[0][2]、dp[1][3]、dp[2][4]、...。例如计算dp[0][2],若s0=s2,则有dp[0][2]=dp[1][1]=0,这时用到了前面得到的dp[1][1]。(4)第三轮递推:计算长度为4的子串的dp[][],即计算出dp[0][3]、dp[1][4]、dp[2][5]、...。例如计算dp[0][3],若s0=s3,则有dp[0][3]=dp[1][2],这时用到了前面得到的dp[1][2]。(5)继续递推,最后得到所有的dp[][]。22s=input()dp=[[0]*5010for_inrange(5010)]ans=0forkinrange(1,len(s)):#第k轮递推。k=j-iforiinrange(len(s)-k):#子串从s[i]开始

j=i+k#子串末尾是s[j]ifs[i]>s[j]:dp[i][j]=1ifs[i]<s[j]:dp[i][j]=0ifs[i]==s[j]:dp[i][j]=dp[i+1][j-1]ifdp[i][j]==1:ans+=1print(ans)用循环变量k表示第k轮递推

罗勇军8.4

DP背包蓝桥杯算法入门最经典的DP问题:0/1背包给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包的容量为C。背包问题:选择装入背包的物品,使得装入背包中物品的总价值最大。如果在选择装入背包的物品时,每种物品i只有两种选择:装入背包或不装入背包,称为0/1背包问题。24模型设xi表示物品i装入背包的情况,

xi=0,表示物品i没有被装入背包

xi=1,表示物品i被装入背包约束条件:目标函数:25暴力法组合问题,每个物品有装进背包和不装进背包两种选择,n个物品有2n种组合,在这2n种组合中找到最大价值的组合。例如有5个物品,用二进制帮助求组合,就是{00000,00001,00010,...,11110,11111)共25种组合。

暴力法:写n个for,每个物品取、不取两种情况。26例:有5个物品,重量分别是{2,2,6,5,4},价值分别为{6,3,5,4,6},背包的容量为10。定义状态:二维表dp[][]dp[i][j]:表示把前i个物品装入容量为j的背包中获得的最大价值。27填表:按只放第1个物品、只放前2个、只放前3个......一直到放完,这样的顺序考虑。(从小问题扩展到大问题)1、只装第1个物品。(横向是递增的背包容量)28292、只装前2个物品。如果第2个物品重量比背包容量大,那么不能装第2个物品,情况和只装第1个一样。如果第2个物品重量小于背包容量,那么:(1)如果把物品2装进去(重量是2),那么相当于只把1装到(容量-2)的背包中。(2)如果不装2,那么相当于只把1装到背包中。--取(1)和(2)的最大值。302、只装前2个物品。如果第2个物品重量比背包容量大,那么不能装第2个物品,情况和只装第1个一样。如果第2个物品重量小于背包容量,那么:(1)如果把物品2装进去(重量是2),那么相当于只把1装到(容量-2)的背包中。(2)如果不装2,那么相当于只把1装到背包中。--取(1)和(2)的最大值。312、只装前2个物品。如果第2个物品重量比背包容量大,那么不能装第2个物品,情况和只装第1个一样。如果第2个物品重量小于背包容量,那么:(1)如果把物品2装进去(重量是2),那么相当于只把1装到(容量-2)的背包中。(2)如果不装2,那么相当于只把1装到背包中。--取(1)和(2)的最大值。322、只装前2个物品。如果第2个物品重量比背包容量大,那么不能装第2个物品,情况和只装第1个一样。如果第2个物品重量小于背包容量,那么:(1)如果把物品2装进去(重量是2),那么相当于只把1装到(容量-2)的背包中。(2)如果不装2,那么相当于只把1装到背包中。--取(1)和(2)的最大值。332、只装前2个物品。如果第2个物品重量比背包容量大,那么不能装第2个物品,情况和只装第1个一样。如果第2个物品重量小于背包容量,那么:(1)如果把物品2装进去(重量是2),那么相当于只把1装到(容量-2)的背包中。(2)如果不装2,那么相当于只把1装到背包中。--取(1)和(2)的最大值。34按这样的规律一行行填表,直到结束。现在回头考虑,装了哪些物品。看最后一列,15>14,说明装了物品5,否则价值不会变化。DP复杂度?状态转移方程(1)第i个物品的体积比容量j还大,不能装进容量j的背包。那么直接继承前i-1个物品装进容量j的背包的情况即可,状态转移方程:dp[i][j]=dp[i-1][j](2)第i个物品的体积比容量j小,能装进背包。继续分为两种情况:装或者不装第i个。1)装第i个。从前i-1个物品的情况下推广而来,前i-1个物品是dp[i-1][j]。第i个物品装进背包后,背包容量减少c[i],价值增加w[i]。

dp[i][j]=dp[i-1][j-c[i]]+w[i]2)不装第i个。那么:dp[i][j]=dp[i-1][j]。取1)和2)的最大值,状态转移方程:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])3536n,C=map(int,input().split())#物品数量、背包容量c=[0]*(n+1)#物品体积w=[0]*(n+1)#物品价值foriinrange(1,n+1):c[i],w[i]=map(int,input().split())

#第i个物品的体积和价值dp=[[0]*(C+1)for_inrange(n+1)]foriinrange(1,n+1):forjinrange(C+1):ifc[i]>j:

#第i个物品比背包还大,装不了

dp[i][j]=dp[i-1][j]else:#第i个物品可以装

dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])print(dp[n][C])#输出最优解填写多维表格来完成,编码时用若干for循环语句填表。根据表中的结果,逐步计算出大问题的解决方案。(1)递推代码37N=3011dp=[[0]*(N)for_inrange(N)]w=[0]*Nc=[0]*Ndefsolve(i,j):ifdp[i][j]!=0:returndp[i][j]ifi==0:return0ifc[i]>j:dp[i][j]=solve(i-1,j)else:dp[i][j]=max(solve(i-1,j),solve(i-1,j-c[i])+w[i])returndp[i][j]n,C=map(int,input().split())foriinrange(1,n+1):c[i],w[i]=map(int,input().split())print(solve(n,C))先考虑大问题,再缩小到小问题,用递归实现。为避免递归时重复计算子问题,可以用“记忆化”,在子问题得到解决时,就保存结果,再次需要这个结果时,直接返回保存的结果。 (2)递归代码代码例题8.3:2022【lanqiaoOJ2186】38问题描述:将2022拆分成10个互不相同的正整数之和,总共有多少种拆分方法?注意交换顺序视为同一种方法。39这一题是0/1背包:背包容量为2022,物品体积为1~2022,往背包中装10个物品,要求总体积为2022,问一共有多少种方案。定义dp[][][]:dp[i][j][k]表示数字1~i取j个和为k的方案数。状态转移:从i-1扩展到i,分两种情况:(1)k≥i。数i可以要,也可以不要。要i。从1~i-1中取j-1个数,再取i,等价于dp[i-1][j-1][k-i]。

不要i。从1~i-1中取j个数,等价于dp[i-1][j][k]。

合起来:dp[i][j][k]=dp[i-1][j][k]+dp[i-1][j-1][k-i]。(2)k<i。由于数i比总和k还大,显然i不能用,有dp[i][j][k]=dp[i-1][j][k]。题解40dp=[[[0]*2024foriinrange(11)]forjinrange(2024)]foriinrange(0,2023):dp[i][0][0]=1#特别注意这个初始化foriinrange(1,2023):forjinrange(1,11):#注意:j从小到大,或从大到小都行

forkinrange(1,2023):ifk<i:dp[i][j][k]=dp[i-1][j][k]#无法装进背包

else:dp[i][j][k]=dp[i-1][j][k]+dp[i-1][j-1][k-i]print(dp[2022][10][2022])完全背包41完全背包的定义:给定一个背包和n种物品,背包容量为C,第i种物品的体积为ci,价值为wi,数量有无限多个;选择一些物品装入背包,在不超过背包容量C的情况下,使得背包中物品的总价值最大。例题8.5:疯狂的采药https:///problem/P161642问题描述:药童Li去山洞里采药。山洞里有一些不同种类的草药,每种草药可以无限制地疯狂采摘。采每一种都需要一些时间,每一种也有它自身的价值。给Li一段时间,在这段时间里,让采到的草药的总价值最大。输入:输入第一行有两个整数,分别代表总共能够用来采药的时间C和代表山洞里的草药的数目n。第2到第(n+1)行,每行两个整数,第(i+1)行的整数ci,wi分别表示采摘第i种草药的时间和该草药的价值。输出:输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。输入样例:7037110069112输出样例:140对于30%的数据,保证n≤1000。对于100%的数据,保证1≤n≤10000,1≤C≤107,且1≤n×C≤107,1≤ci,wi≤10000。题解43把题目中的采药时间C看成背包容量,把草药看成物品,建模为背包问题。完全背包的解题思路和0/1背包类似。定义状态dp[][]:dp[i][j]表示把前i种物品(从第1种到第i种)装入容量为j的背包中获得的最大价值。把每个dp[i][j]都看成一个背包:背包容量为j,装1~i这些物品。最后得到的dp[n][C]就是问题的答案:把n种物品装进容量T的背包的最大价值。在0/1背包问题中,每个物品只有拿与不拿两种;而完全背包问题需要考虑拿几个,每种物品有无穷多件,第i种可以装0件、1件、2件、...、C/ci件。44C,n=map(int,input().split())c=[0]*(n+1)w=[0]*(n+1)foriinrange(1,n+1):c[i],w[i]=map(int,input().split())dp=[[0]*(C+1)for_inrange(n+1)]foriinrange(1,n+1):forjinrange(1,C+1):dp[i][j]=dp[i-1][j]forkinrange(j//c[i]+1):dp[i][j]=max(dp[i][j],dp[i-1][j-k*c[i]]+k*w[i])print(dp[n][C])代码和0/1背包的代码极为相似,只是多了一个k循环,用来遍历每种物品拿几个。45C,n=map(int,input().split())c=[0]*(n+1)w=[0]*(n+1)foriinrange(1,n+1):c[i],w[i]=map(int,input().split())dp=[[0]*(C+1)for_inrange(n+1)]foriinrange(1,n+1):forjinrange(1,C+1):dp[i][j]=dp[i-1][j]ifj>=c[i]:dp[i][j]=max(dp[i][j],dp[i][j-c[i]]+w[i])print(dp[n][C])优化:把k循环去掉k本身也是一个动态规划的递推过程,k从0递推到1、2、...,计算dp[i][j]时,它前面的dp[i][j-c[i]]也包含了取0件、1件、2件、...的计算结果,所以用不着再用k循环。分组背包46分组背包的定义:给定一个背包,背包容量为C,有n组物品,其中第i组第k个物品的体积为cik,价值为wik;每组最多只能选一个物品装入背包,在不超过背包容量C的情况下,使得背包中物品的总价值最大。例题8.6:通天之分组背包https:///problem/P175747问题描述:自01背包问世之后,小A对此深感兴趣。一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为n组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。输入:两个数C,x,表示一共有x件物品,总重量为C。接下来x行,每行3个数ai,wi,pi,表示物品的重量,利用价值,所属组数。输出:一个数,最大的利用价值。输入样例:453101011051504002输出样例:100≤C,x≤1000,1≤n≤100,ai,wi,pi都是int。48分组背包的解题思路也与0/1背包相似。0/1背包的状态dp[i][j],表示把前i个物品(从第1个到第i个)装入容量为j的背包中获得的最大价值。类似地定义分组背包的状态dp[i][j],它表示把前i组物品装进容量j的背包(每组最多选一个物品),可获得的最大价值。状态转移方程是:

dp[i][j]=max{dp[i-1][j],dp[i-1][j-c[i][k]]+w[i][k]}方程中,dp[i-1][j]表示第i组不选物品,dp[i-1][j-c[i][k]]表示第i组选第k个物品。求解方程需要做i、j、k的三重循环。49cnt=[0]*101#cnt[i]:第i组有多少个物品w=[[0]*1001for_inrange(101)]#w[i][k]:第i组第k个物品的价值c=[[0]*1001for_inrange(101)]#c[i][k]第i组第k个物品的重量dp=[[0]*1001for_inrange(101)]C,x=map(int,input().split())n=0#共n组for_inrange(x):a,b,p=map(int,input().split())cnt[p]+=1#第p组的物品数量是cnt[p]v=cnt[p]#p组共v个物品,当前是p组的第v个物品

c[p][v]=a#第p组第v个的物品的重量

w[p][v]=b#第p组第v个的物品的价值

n=max(n,p)#最大组号作为组数foriinrange(1,n+1):#第i组物品,共n组

forjinrange(C+1):#背包容量是jforkinrange(cnt[i]+1):#第i组的第k个

ifj>=c[i][k]:#c[i][k]能放进背包jdp[i][j]=max(dp[i][j],\max(dp[i-1][j],dp[i-1][j-c[i][k]]+w[i][k]))print(dp[n][C])罗勇军8.5

DP例题蓝桥杯算法入门例题8.7子串简写LangqiaoOJ351451问题描述:一种很新的简写方法:对于一个字符串,只保留首尾字符,将首尾字符之间的所有字符用这部分的长度代替。例如internationalization简写成i18n,Kubernetes简写成K8s,Lanqiao简写成L5o等。在本题中,我们规定长度大于等于K的字符串都可以采用这种简写方法(长度小于K的字符串不配使用这种简写)。给定一个字符串S和两个字符c1和c2,请你计算S有多少个以c1开头,c2结尾的子串可以采用这种简写?输入:第一行包含一个整数K。第二行包含一个字符串S和两个字符c1和c2。输出:一个整数代表答案。52定义状态dp[]:dp[i]表示到s[1]~s[i]中首字母c1出现的次数。DP转移:遍历s时,若出现尾字母c2,那么答案累加dp[i-k+1]。只需遍历一次字符串,计算复杂度为O(n)。53k=int(input())s,c1,c2=input().split()len_s=len(s)s='0'+s#s[0]不用,从s[1]开始存字符串,容易理解ans=0dp=[0]*(len_s+1)foriinrange(1,len_s+1):ifs[i]==c1:dp[i]=dp[

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论