一、斐波那契数列
题目: 牛客斐波那契队列题目
思路:
f
(
x
)
=
{
1
x
=
1
,
2
f
(
x
−
1
)
+
f
(
x
−
2
)
x
>
2
f(x) = \begin{cases} 1 &x=1,2 \\ f(x-1) + f(x-2) &x>2 \end{cases}
f(x)={1f(x−1)+f(x−2)x=1,2x>2
第一想法:递归
存在问题:
- 计算 f ( x ) f(x) f(x)时要计算 f ( x − 1 ) f(x-1) f(x−1)和 f ( x − 2 ) f(x-2) f(x−2)
- 计算 f ( x − 1 ) f(x-1) f(x−1)时,要计算 f ( x − 2 ) f(x-2) f(x−2)和 f ( x − 3 ) f(x-3) f(x−3)
- 以此类推,中间有无数个子问题需要重复计算
解决方法:动态规划
- 预设 f ( 1 ) f(1) f(1)、 f ( 2 ) f(2) f(2),从 f ( 3 ) f(3) f(3)开始计算
- 保留每个计算出的 f ( x ) f(x) f(x),这样每次计算,只需要使用之前的值
代码:
class Solution {
public:
int Fibonacci(int n) {
if (n == 0)return 0;
if (n <= 2) return 1;
vector<int> befData;
//保存f(1) f(2)
befData.push_back(1);
befData.push_back(1);
for (int i = 2; i < n; i++) {
befData.push_back(befData[(i - 1)] + befData[(i - 2)]);
}
return befData[befData.size() - 1];
}
};
优化:只保留第 x x x项的前两项结果,也就是 f ( x − 1 ) f(x-1) f(x−1)和 f ( x − 2 ) f(x-2) f(x−2)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
使用公式:
[
f
(
n
)
f
(
n
−
1
)
f
(
n
−
1
)
f
(
n
−
2
)
]
=
[
1
1
1
0
]
n
−
1
{\left[ \begin{matrix} f(n) &f(n-1) \\ f(n-1) &f(n-2) \end{matrix} \right]} = {\left[ \begin{matrix} 1 &1 \\ 1 &0 \end{matrix} \right]}^{n-1}
[f(n)f(n−1)f(n−1)f(n−2)]=[1110]n−1
斐波那契数列的扩展问题:跳台阶问题
二、跳台阶
题目: 牛客跳台阶题目扩展
分析:
青蛙跳到最后一阶台阶时,可以选择跳一步或者跳两步,也就是:
r
e
s
[
n
]
=
{
r
e
s
[
n
−
1
]
+
1
r
e
s
[
n
−
2
]
+
1
res[n] = {\begin{cases} res[n-1] + 1 \\ res[n-2] + 1 \end{cases}}
res[n]={res[n−1]+1res[n−2]+1
所以,跳到最后一阶台阶的总跳法为跳到第
n
−
1
n-1
n−1个台阶的跳法加上跳到第
n
−
2
n-2
n−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)
显而易见,斐波那契数列
代码:
#include <iostream>
using namespace std;
int f(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
int n_1 = 1, n_2 = 1, res = 0;
for (int i = 3; i <= n; i++) {
res = n_1 + n_2;
n_2 = n_1;
n_1 = res;
}
return res;
}
int main() {
int n = 0;
scanf("%d", &n);
printf("%d\n", f(n+1));
}
三、跳台阶扩展
题目: 牛客跳台阶扩展题目
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
思路:跳最后一个台阶时,可以为倒数第二个台阶上跳一个台阶,也可以为倒数第三个台阶跳两个,也可以倒数第四个台阶跳三个,…,可以为第0个台阶处直接跳n个,也就是:
r
e
s
[
n
]
=
{
r
e
s
[
n
−
1
]
+
1
跳一个台阶
r
e
s
[
n
−
2
]
+
1
跳两个台阶
r
e
s
[
n
−
3
]
+
1
跳三个台阶
.
.
.
r
e
s
[
0
]
+
1
跳
n
个台阶
res[n] = { \begin{cases} res[n-1] + 1 &跳一个台阶\\ res[n-2] + 1 &跳两个台阶\\ res[n-3] + 1 &跳三个台阶\\ ... \\ res[0] + 1 &跳n个台阶 \end{cases} }
res[n]=⎩
⎨
⎧res[n−1]+1res[n−2]+1res[n−3]+1...res[0]+1跳一个台阶跳两个台阶跳三个台阶跳n个台阶
所以:
f
(
n
)
=
∑
n
=
0
n
−
1
f
(
i
)
f(n) = \sum_{n=0}^{n-1}{f(i)}
f(n)=n=0∑n−1f(i)
也就是说:
f
(
n
−
1
)
=
∑
n
=
0
n
−
2
f
(
i
)
f(n-1) =\sum_{n=0}^{n-2}{f(i)}
f(n−1)=n=0∑n−2f(i)
则:
f
(
n
)
=
f
(
n
−
1
)
+
∑
n
=
0
n
−
2
f
(
i
)
f(n)=f(n-1) +\sum_{n=0}^{n-2}{f(i)}
f(n)=f(n−1)+n=0∑n−2f(i)
f ( n ) = 2 ∗ f ( n − 1 ) f(n) = 2*f(n-1) f(n)=2∗f(n−1)
代码:
#include <iostream>
#include <vector>
using namespace std;
int f(int n) {
int n_1 = 1;
int res = 1;
for(int i = 1; i < n;i++){
n_1 = res;
res = n_1 * 2;
}
return res;
}
int main() {
int n = 0;
scanf("%d", &n);
printf("%d\n", f(n));
}
// 64 位输出请用 printf("%lld")
四、矩形覆盖问题
题目: 牛客矩形覆盖问题
分析:
假设
2
∗
n
2*n
2∗n面积的矩形为
r
e
s
[
n
]
res[n]
res[n],则,如果第一块矩形选择竖着覆盖,
r
e
s
[
n
]
=
r
e
s
[
n
−
1
]
+
1
res[n] = res[n-1] + 1
res[n]=res[n−1]+1,如果第一块矩形选择横着覆盖,则,
r
e
s
[
n
]
=
r
e
s
[
n
−
2
]
+
1
res[n] = res[n-2] + 1
res[n]=res[n−2]+1(每一次横着覆盖都必须是两个小矩形同时横着覆盖),也就是:
r
e
s
[
n
]
=
{
r
e
s
[
n
−
1
]
+
1
r
e
s
[
n
−
2
]
+
1
res[n] = \begin{cases} res[n-1] + 1 \\ res[n-2] + 1 \end{cases}
res[n]={res[n−1]+1res[n−2]+1
所以:
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n) = f(n-1) + f(n-2)
f(n)=f(n−1)+f(n−2)
显而易见,还是个斐波那契数列问题