引入
递推≈简单的动态规划
-
递推是动态规划的基础:动态规划可以看作是递推的一种扩展和优化 递推通过递推公式逐步推导出问题的解 而动态规划则通过存储子问题的解来避免重复计算 从而提高效率
-
动态规划解决最优化问题:动态规划通常用于解决最优化问题 而递推则更多地用于解决一些简单的数学问题
-
状态和状态转移方程:动态规划的核心在于定义状态和状态转移方程 而递推则更多地依赖于递推公式
一、斐波拉契数列问题模型
有雌雄各一的一对兔子,2 个月后就会成熟,成熟后,每个月都会繁殖雌雄各一的一对兔子
问题:过n个月后有多少对兔子?
第四个月的兔子来源于,第三个月的兔子数量,以及第四个月新出生的兔子。
第四个月新出生的兔子的数量和两个月前的兔子数量有关,因为两个月前的兔子在这个月均已成熟,在这个月可以生下小兔子。
所以假设我们定义f[]表示第i个月的兔子数量,则能得到关系式:
fi] = f[i-1]+f[i-2]
二、斐波那契数与特殊值
斐波那契数列为1、1、2、3、5、8、13、......
特殊值:
●斐波那契数列第46项为1836311903,这是int能存储的下的斐波那契数列的项中的最大值
●斐波那契数列第92项为7540113804746346429,这是long long能存储的下的斐波那契数列的项中的最大值
三、斐波那契数列的边界、状态、递推式、目标
1、状态:f[i]表示第i个月兔子的数量 第i个月兔子的数量=第i-1个月兔子数量+新繁殖的兔子数量 新繁殖的兔子数量=第i-2个月兔子数量
2、状态转移方程:(递推关系) f[i]=f[i-1]+f[i-2]
3、边界:(递推的起点) f[1]=1; f[2]=1;
4、目标:(递推的终点) f[n]
四、课堂练习
课堂练习1 1028 神奇的斐波那契数列
cpp
1028 神奇的斐波那契数列
经验值:400
时间限制:1000毫秒
内存限制:128MB
题目描述 Description
斐波那契数列,又称黄金分割数列,指得是这样一个数列:数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和,即:1, 1, 2, 3, 5, 8......现给出一个正整数N,求斐波那契数列中第N项是多少?
输入描述 Input Description
输入一行,为一个正整数N(1≤N≤46)。
输出描述 Output Description
输出一行,为斐波那契数列中的第N项的数值。
样例输入 Sample Input
12
样例输出 Sample Output
144
cpp
/*1028
斐波那契数列:
每一项是前两项的和
递归的四要素:
状态:f[i]:就是数组元素的含义
状态转移方程:递推关系
边界:递推的起始项
目标:递推的终点
状态:f[i] 表示第i项 的数
状态转移方程:
f[i] = f[i-1] + f[i-2]
边界:
f[1] = 1;
f[2] = 1;
目标:f[n]
int 范围能存下斐波那契数列第46项:1836311903
long long 范围能存下斐波那契数列第92项:7540113804746346429
*/
#include<iostream>
using namespace std;
int n, f[50];
int main(){
cin >> n;
f[1] = f[2] = 1; //边界
for(int i=3; i<=n; i++){
f[i] = f[i-1] + f[i-2];
}
cout << f[n];
return 0;
}
课堂练习2 12034 疯狂的小老鼠
cpp
12034 疯狂的小老鼠
经验值:1600
时间限制:1000毫秒
内存限制:128MB
题目描述 Description
有一对刚刚出生的小老鼠,从出生后长4个月变成成年的大老鼠,每一对成年的大老鼠每个月都生一对小老鼠,小老鼠4个月后又变成成年的大老鼠,之后每个月又生一对小老鼠。
假设第1个月初有一对已成年的大老鼠,问第d个月的老鼠数为多少对(假设老鼠不会死亡)?
输入描述 Input Description
一个整数d(1=<d<=90),表示第d个月。
输出描述 Output Description
一个整数,表示老鼠的对数。
样例输入 Sample Input
12
样例输出 Sample Output
69
cpp
/*
f[i]: 第i个月的老鼠数量:
上个月的老鼠数量+ 4个月之前的老鼠数量
f[i]=f[i-1]+f[i-4]
边界:
for(int i=1;i<=4;i++){
f[i]=i+1;
}
for(int i=1;i<=3;i++){
f[i]=i+1;
}
*/
#include <iostream>
using namespace std;
long long f[95];
int main(){
int d;
cin>>d;
//边界
for(int i=1;i<=4;i++){
f[i]=i+1;
}
//递推关系
for(int i=5;i<=d;i++){
f[i]=f[i-1]+f[i-4];
}
cout<<f[d];
return 0;
}
课堂练习3 12032 建筑师酷丁猫
cpp
12032 建筑师酷丁猫
经验值:1600
时间限制:1000毫秒
内存限制:128MB
题目描述 Description
我们都知道n个 3 * 1 的小矩形和一个 3 * n 的大矩形总面积是一样的。
现在酷丁猫是一个建筑师,他想用n块 3 * 1 的砖块,堆砌一面 3 * n 的墙,总共有多少种堆砌的方法?
输入描述 Input Description
一个整数n,表示3 * n的墙
输出描述 Output Description
一个整数,表示方法的数量
样例输入 Sample Input
6
样例输出 Sample Output
6
数据范围及提示 Data Size & Hint
n<=40
cpp
#include <iostream>
using namespace std;
long long f[10005];
int n;
int main(){
int n;
cin>>n;
f[1]=1;
f[2]=1;
f[3]=2;
for(int i = 4;i<=n;i++){
f[i]=f[i-1]+f[i-3];
}
cout<<f[n];
return 0;
}
课堂练习4 2937 楼梯
cpp
2937 楼梯
经验值:1200
时间限制:1000毫秒
内存限制:128MB
题目描述 Description
有 N 级的楼梯,你一开始在底部,每次可以向上迈最多 K 级楼梯(最少 1 级),问到达第 N 级楼梯有多少种不同方式。
输入描述 Input Description
两个正整数N,K
输出描述 Output Description
一个正整数,为不同方式数,由于答案可能很大,你需要输出 ans mod 100003 后的结果。
样例输入 Sample Input
5 2
样例输出 Sample Output
8
数据范围及提示 Data Size & Hint
N<=100,000,K<=100
cpp
#include<iostream>
using namespace std;
int n,k,f[100005],sum=1;
int main(){
cin>>n>>k;
f[0]=1;
for(int i=1;i<=n;i++){
for(int j=max(0,i-k);j<=i-1;j++){
f[i]+=f[j];
}
f[i]%=100003;
}
cout<<f[n]%100003;
return 0;
}
课堂练习5 4366搬砖(rock)
cpp
4366 搬砖(rock)
经验值:1600
时间限制:1000毫秒
内存限制:128MB
瑶海区2019年信息学竞赛试题
不许抄袭,一旦发现,直接清空经验!
题目描述 Description
考古队发现了一个非常巨大的古墓,具有非常高的考古价值,你随队来到了考古现场。经过紧张的发掘,古墓的墓道终于显露出来,但是它被一块块方砖封住了,现在你的任务就是帮助考古队将这些方砖移走,打通墓道。由于这些保存完好的古代方砖也是珍贵的文物,所以规定一次最多只能搬三块砖。那将这些砖头搬走共有多少种不同的搬法。
例如,现在总共有4个砖头,那么可以选择的方法有以下7种:
1,1,1,1(分4次搬完,每次搬一个砖头)
1,2,1(分3次搬完,第一次搬一个,第二次搬两个,第三次搬一个)
1,1,2(分3次搬完,第一次搬一个,第二次搬一个,第三次搬两个)
2,1,1(分3次搬完,第一次搬两个,第二次搬一个,第三次搬一个)
2,2 (分2次搬完,第一次搬两个,第二次搬两个)
1, 3 (分2次搬完,第一次搬一个,第二次搬三个)
3, 1 (分2次搬完,第一次搬三个,第二次搬一个)
输入描述 Input Description
一个1~40的正整数N,表示共有N块砖头
输出描述 Output Description
一个正整数,表示N块砖头移动的方法数
样例输入 Sample Input
4
样例输出 Sample Output
7
数据范围及提示 Data Size & Hint
1<=n<=40
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n;
cin>>n;
long long a[95]={0,1,2,4};
for(int i=4; i<=n; i++){
a[i]=a[i-1]+a[i-2]+a[i-3];
}
cout<<a[n];
return 0;
}
课堂练习6 2752 爬楼梯
cpp
2752 爬楼梯
经验值:1200
时间限制:1000毫秒
内存限制:128MB
瑶海区2016年信息学竞赛试题
不许抄袭,一旦发现,直接清空经验!
题目描述 Description
小明和同学们顺利闯过了第二关,进入第三关的时候,小明和同学们发现了一个楼梯,可是有些楼梯坏了。智慧之门门主说,只要同学们通过这个楼梯就可以游玩里面的项目,可是同学们必须为我解决一个问题,这个时候大家都迫不及待的让智慧之门门主快点说问题。智慧之门门主指着面前的楼梯说,你可以一步一级,或一步二级,也可以一步三级,但是某些台阶是坏的,即脚不能踩在上面,哪位同学能求出登上最高台阶的总方案数。小明和同学在思考?
输入描述 Input Description
第一行两个整数,第一个数为楼梯总级数n(n<50),第二个数表示有几个坏台阶m
第二行连续m个整数为坏台阶的级数。注意:数据的规模在longint范围内。
输出描述 Output Description
一个整数,表示登上最高台阶的方案数
样例输入 Sample Input
7 2
4 6
样例输出 Sample Output
6
cpp
#include<bits/stdc++.h>
using namespace std;
long long b[55];
long long a[55]={1};
int n,t,m;
int main(){
cin>>n>>m;
while(m--){
cin>>t;
b[t]=1;
}
for(int i=1; i<=n; i++){
if(b[i]==1) continue;
for(int j=1; j<=3; j++){
if(i-j>=0) a[i]+=a[i-j];
}
}
cout<<a[n];
return 0;
}
课堂练习7 1021 分数序列之和
cpp
1021 分数序列之和
经验值:800
时间限制:1000毫秒
内存限制:128MB
题目描述 Description
已知一分数序列,b1/a1、b2/a2、b3/a3、b4/a4、b5/a5、......,其中b[i+1] = bi+ai,a[i+1]=bi,取b1=2,a1=1。如这个序列前6项分别是2/1,3/2,5/3,8/5,13/8,21/13。求这个分数序列的前n项之和。
输入描述 Input Description
输入有一行,包含一个正整数n(n <= 30)
输出描述 Output Description
输出有一行,包含一个浮点数,表示分数序列前n项的和,精确到小数点后5位(四舍五入)
样例输入 Sample Input
2
样例输出 Sample Output
3.50000
cpp
#include<bits/stdc++.h>
using namespace std;
int a[35]={1,1};
double ans;
int main(){
int n;
cin>>n;
for(int i=2; i<=n+1; i++){
a[i]=a[i-1]+a[i-2];
ans+=a[i]*1.0/a[i-1];
}
printf("%.5f",int(ans*100000+0.5)/100000.0);
return 0;
}