赞
踩
在数学上,斐波纳契数列以如下递归方式被定义:
F ( 0 ) = 1 , F ( 1 ) = 1 , … , F ( n ) = F ( n − 1 ) + F ( n − 2 ) ( n ≥ 2 , n ∈ N + ) F(0)=1, F(1)=1, … , F(n)=F(n-1)+F(n-2) \quad (n \ge 2, n \in N^+) F(0)=1,F(1)=1,…,F(n)=F(n−1)+F(n−2)(n≥2,n∈N+)
根据定义,其前十项为 1, 1, 2, 3, 5, 8, 13, 21, 34, 55。
假设现在给出一个编程问题:输入一个自然数 n n n,要求输出斐波那契数列的第 n n n 项 ( 0 ≤ n ≤ 1000000 ) (0≤n≤1000000) (0≤n≤1000000)。如果我们根据定义来求解本题,那么可以直接写出以下代码:
int Fibonacci(int n)
{
if(n==0 || n==1) return 1;
else return Fibonacci(n-1)+Fibonacci(n-2);
}
假设现在输入 n = 7 n=7 n=7,那么该程序的执行过程如下图所示:
上图表明:
直到最终迭代到了 n = 1 n=1 n=1 和 0 0 0。
而在这上面的计算中,我们发现其有大量的重复计算,比如红框部分。出现这样的现象的原因是,我们对于前面计算出的结果并没有将其记录下来,从而导致了重复计算。为了解决这个问题,我们可直接在递归的过程中,将所有结果都保存下来,改进后的代码如下
const int N=100000;
int fibonacci[N];
int Fibonacci(int n)
{
fibonacci[0]=fibonacci[1]=1;
for(int i=2;i<=n;i++)
fibonacci[i]=fibonacci[i-1]+fibonacci[i-2];
return fibonacci[n];
}
可以看出,改进后的算法其时间复杂度和空间复杂度均为 O ( n ) O(n) O(n)。还能不能继续优化呢?答案是可以。
考虑到斐波那契数列的第 n n n 项实际上仅仅只依赖于其前两项,那么我们也没必要开那么大的数组来把前面的每一步结果都保存下来,因此再进一步优化的代码如下:
int Fibonacci(int n)
{
int a=1,b=1,temp;
for(int i=2;i<=n;i++){
temp=a+b;
a=b,b=temp;
}
return b;
}
由于斐波那契数列本身正代表着动态规划这一算法中的入门,因此我们通常把具有斐波那契数列这种特性的题目统称为斐波那契问题。而斐波那契问题在动态规划中也占有相当一部分比例,下面列出几个较为经典的例子。
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n n n 级的台阶总共有多少种跳法。
我们来寻找递推关系,由题可知青蛙在每次跳跃时都会有以下两种方式:
而在 3 阶及 3 阶以上的情况下(假设为 n n n 阶),我们都可以将这种情况拆分为 1 和 2 这两种情况之和。逆向思考:假设青蛙要在最后跳上第 n n n 阶,那么其可以是从第 n − 1 n-1 n−1 阶跳 1 1 1 阶来到这里,也可以是从第 n − 2 n-2 n−2 阶跳 2 2 2 阶来到这里,有且仅有这两种跳法。因此,我们便找到了递推公式: f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2)。不难发现,这个递推公式和斐波那契数列中的公式一致,因此其代码也是一致的,在此就不再给出。
有一条长度为 N ( 1 ≤ N ≤ 10 ) N(1≤N≤10) N(1≤N≤10) 的小路,现给两种不同地板:一种长度为 1,另一种长度为 2,数目不限。若要将这个长度为 N N N 的小路都铺上地板,问有多少种不同的铺法?
例如,长度为 4 的地面一共有如下 5 种铺法:
当
N
=
1
N=1
N=1 时,显然只有一种方案;
当
N
=
2
N=2
N=2 时,有 2 两种方案(分别是 2=1+1 和 2=2);
当
N
≥
3
N≥3
N≥3 时(假设为
n
n
n),对于当前
f
(
n
)
f(n)
f(n) 这个状态,其前面的状态只有两种(和青蛙跳台阶的实质相同):
因此,我们便找到了递推式:
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n) = f(n-1) + f(n-2)
f(n)=f(n−1)+f(n−2)。
可以发现,这个递推公式和斐波那契数列中的公式一致,因此其代码也是一致的,在此就不再给出。
假设可以用
2
×
1
2\times1
2×1 的小矩形横着或竖着去覆盖更大的矩形(类似于在一个房间中贴瓷砖)。请问用
n
n
n 个
2
×
1
2\times1
2×1 的小矩形无重叠地覆盖一个
2
×
n
2\times n
2×n 的大矩形,总共有多少种方法?
依然来寻找递推关系:
当 n = 1 n=1 n=1 时,显然只有一种;
当
n
=
2
n=2
n=2 时,有两种(如下图所示):
n
=
3
n=3
n=3时,有三种(如下图所示):
当
n
>
3
n>3
n>3 时(假设为
k
k
k),怎么做呢?
有且仅有两种办法:从
k
−
1
k-1
k−1 的地方竖着放一块、从
k
−
2
k-2
k−2 的地方横着放两块。
这样一看,就又回到了斐波那契式递归上了,即:
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n) = f(n-1) + f(n-2)
f(n)=f(n−1)+f(n−2)。
给定一个由
0
−
9
0-9
0−9 组成的字符串,1 可以转化成 A,2 可以转化成 B。依此类推。25 可以转化成 Y,26 可以转化成 Z,给一个字符串,返回能转化的字母串的有几种?
比如,123的转换方式有:
即共有三种可能的转换(ABC、LC、AW),即返回 3。
又比如 99999 只有一种转换:IIIII,故返回 1。
其实本题的实质是求在第
k
k
k 个位置及其之前的数字串共能转化为多少种字母序列。
对第
k
k
k 个数字而言,其仅有两种转换方法:
根据这两个转换规则,我们在程序中可以将其描述为一个遍历数字字符串的过程,其算法流程如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。