这是通往幼儿园的车,佬请主动下车右转!
目录
引子
很久很久以前,我们的小斐波那契养了一对小兔子,哦,这两只贪吃的小兔子在某个阳光明媚的早晨,不小心吃了一株基因突变的大白菜而发生了基因突变,于是乎这两只小兔子及其后代获得了以下能力:
1、永生
2、繁殖周期固定,且一次繁殖只能生出一对小兔子,与此同时小兔子出生后的第一周期不具备繁殖能力
也就是说第一周期小兔子没有繁殖能力,所以还是一对;第二周期生下一对小兔于是共有两对小兔;第三周期老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对……
小斐波那契因为没有作业,闲的没事干,于是开始无聊地数兔子,他发现,兔子对数按周期数呈这样一个数列:1、1、2、3、5……聪明的小斐波那契自然也发现了数列的规律:前面相邻两项之和,构成了后一项。小斐波那契十分高兴,他决定将数列用自己的名字命名,于是乎著名的“斐波那契数列”诞生了,它具有以下美妙而简单的性质:
1、
2、
深度优先搜索
一不小心小斐波那契到了上高中的年纪,他的小兔子在家里生了一窝又一窝,每次回到家小斐波那契都很苦恼:因为兔子繁殖的速度超过了他学算数的速度,他已经数不清自己家里有多少对兔子了,但是他还记得小兔子已经繁殖了多少周期,他想请你教教他,算算自己家里究竟有了多少对兔子,这决定着小兔子们的死活——究竟是被吃掉,还是被吃掉。
#include<bits/stdc++.h>
using namespace std;
int dfs(int x){
if(x==1) return 1;
else if(x==2) return 1;
return dfs(x-1)+dfs(x-2);
}
int main(){
int n;
scanf("%d",&n);
printf("%d",dfs(n));
return 0;
}
小斐波那契十分高兴,因为算出来的结果显示小斐波那契的家足够容纳更多的小兔子,所以他决定暂时不吃掉这些小兔子,直到有一天——
从记忆化搜索到动态规划
小斐波那契发现输入繁殖周期后,自己的电脑计算小兔子的数量需要很久,经过细细推敲,小斐波那契发现重复的、多余的搜索占用了大量的时间,比如:
小斐波那契不想让这些重复的搜索占用自己和兔兔玩耍的时间,他决定更新你的代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
int dfs(int x){
if(x==1) return 1;
else if(x==2) return 1;
if(!a[x]) a[x]=dfs(x-1)+dfs(x-2);
return a[x];
}
int main(){
memset(a,0,sizeof(a));
int n;
scanf("%d",&n);
printf("%d",dfs(n));
return 0;
}
这样子小斐波那契就不用花很多时间在电脑前等结果,而是有很多多余的时间和小兔子玩耍了,不过小斐波那契依然觉得自己的代码不够精简,于是又思考了两天,代码3.0出炉了:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N];
int main(){
memset(a,0,sizeof(a));
a[1]=1;a[2]=1;
scanf("%d",&n);
for(int i=3;i<=n;i++) a[i]=a[i-1]+a[i-2];
printf("%d",a[n]);
return 0;
}
滚动数组优化
经过两次优化,小斐波那契对3.0代码十分满意,不过,追求十全十美的小斐波那契仍然觉得代码有一定的优化空间,譬如……空间复杂度?
#include<bits/stdc++.h>
using namespace std;
int n,a[5];
int main(){
memset(a,0,sizeof(a));
a[1]=1;a[2]=1;
scanf("%d",&n);
for(int i=3;i<=n;i++){
a[3]=a[1]+a[2];
a[1]=a[2];
a[2]=a[3];
}
printf("%d",a[3]);
return 0;
}
尾声
萧炎和纳兰家的三年之约如期而至又过了三年,小斐波那契从高中毕业啦,小兔子也繁殖了六十个周期,毕竟人到了18岁,就不能过得像个小孩子,小斐波那契决定忍痛杀死自己那养了三年的1548008755920只小兔子,请全世界人民吃烤全兔,那么你觉得平均每个人能分到多少多少只兔子呢?