蓝桥杯真题复盘:Fibonacci 数列与黄金分割(C++ 算法详解)
一、 题目描述
题目描述
Fibonacci 数列是非常著名的数列:
- F 1 = 1 F1 = 1 F1=1
- F 2 = 1 F2 = 1 F2=1
- 对于 i > 3 i > 3 i>3, F i = F i − 1 + F i − 2 Fi = Fi - 1 + Fi - 2 Fi=Fi−1+Fi−2
Fibonacci 数列有一个特殊的性质,前一项与后一项的比值, F i F i + 1 \frac{Fi}{Fi + 1} Fi+1Fi,会趋近于黄金分割。
为了验证这一性质,给定正整数 N N N,请你计算 F N F N + 1 \frac{FN}{FN + 1} FN+1FN,并保留 8 位小数。
输入格式
一个正整数 N N N。 ( 1 ≤ N ≤ 2 , 000 , 000 , 000 ) (1 \le N \le 2,000,000,000) (1≤N≤2,000,000,000)
输出格式
F N F N + 1 \frac{FN}{FN + 1} FN+1FN。答案保留 8 位小数。
样例输入
text
2
样例输出
text
0.50000000
二、 完整代码
cpp
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main()
{
long long n;
cin >> n;
// 当 N 较小时,通过递推直接计算
if (n <= 35)
{
long long f[40];
f[1] = 1;
f[2] = 1;
for (long long i = 3; i <= n + 1; i++)
{
f[i] = f[i - 1] + f[i - 2];
}
// 必须显式转换为 double,防止整数相除归零
double res = (double)f[n] / f[n + 1];
cout << fixed << setprecision(8) << res << "\n";
}
else
{
// 当 N 很大时,比值直接趋近于黄金分割常数
double res = (double)(sqrt(5) - 1) / 2;
cout << fixed << setprecision(8) << res << "\n";
}
return 0;
}
三、 我学到的核心细节与避坑指南
在解决这道题的过程中,我踩过了几个非常经典的 C++ 编程与数学陷阱,总结如下:
1. 超大范围数据不能暴力硬算
- 陷阱:题目给出的 N 高达 2,000,000,000(20 亿)。如果用常规的 for 循环递推 20 亿次,必然会遇到时间超限(TLE);如果开大小为 20 亿的数组,会直接内存超限。
- 收获:观察数学性质。斐波那契数列相邻两项的比值在 N 增大到一定程度后会迅速收敛并趋近于黄金分割数 (sqrt(5) - 1) / 2 ≈ 0.61803399。因此,采用分段讨论(小范围递推,大范围直接输出极限值)是解决此类大数据范围题目的标准套路。
2. C++ 整数相除的"归零"陷阱
- 陷阱 :在写
f[n] / f[n + 1]时,由于两边都是 long long 类型的整数,C++ 会执行整型除法,直接舍弃小数部分。因为前面项中分子小于分母,整型相除结果永远是 0。即便后面套了setprecision(8),输出也只会是0.00000000。 - 收获 :在做除法运算时,必须将其中一个操作数强制转换为浮点数(如
(double)f[n] / f[n + 1]),才能触发浮点数高精度除法。
3. 循环边界的细节把控
- 陷阱 :递推时如果写成
for(long long i = 3; i < n; i++),当输入 N = 10 时,循环会在 i = 9 停下,导致无法算出所需的 FN 和 FN + 1。 - 收获 :由于需要用到 FN 和 FN + 1,循环终止条件必须包含 N + 1,即写成
for (long long i = 3; i <= n + 1; i++)。