目录
题目链接:1.类斐波那契循环数 - 蓝桥云课
注:下述题目描述和示例均来自蓝桥云客
题目描述
对于一个有 n 位的十进制数 N = d1d2d3;...dn,可以生成一个类斐波那契数列S,数列 S 的前 n 个数为:
{S1=d1,S2=d2,S3 = d3,...,Sn= dn}
数列 S 的第 k(k > n)个数为:
如果这个数 N 会出现在对应的类斐波那契数列 S 中,那么 N 就是一个类斐波那契循环数。
例如对于 197,对应的数列 S 为:
{1,9,7,17,33,57,107,197,...}
197 出现在 S 中,所以 197 是一个类斐波那契循环数。
请问在 0 至 中,最大的类裴波那契循环数是多少?
解法一:
1. 问题核心理解
类斐波那契循环数的定义:
-
对于一个数字
N = d1d2...dn
,生成数列S
:-
前n项:
S1=d1, S2=d2, ..., Sn=dn
-
后续项:
Sk = S_{k-1} + S_{k-2} + ... + S_{k-n}
-
-
如果
N
出现在数列S
中,则称N
为类斐波那契循环数
2. 代码核心逻辑
isKevinNumber
函数
-
特殊处理:
-
0 和一位数直接返回true(根据定义)
if (N == 0) return true; if (n == 1) return true;
-
-
数字分解:
-
将数字转为字符串,逐字符提取每一位
string s = to_string(N); for (char c : s) digits.push_back(c - '0');
-
-
初始化和校验:
-
计算初始和(前n项的和)
-
如果初始和直接等于或超过N,提前返回
int currentSum = sum(digits); if (currentSum == N) return true; if (currentSum > N) return false;
-
-
动态生成数列:
-
使用双端队列
q
维护最近n项 -
每次计算新项
next = currentSum
-
更新队列:移除最旧项,添加新项,动态维护和
q.push_back(next); currentSum += next - q.front(); q.pop_front();
-
findMaxKevinNumber
函数
-
从1e7向下遍历:找到第一个符合条件的数即为最大值
for (int N = 10000000; N >= 0; --N)
示例验证
以 N = 197
为例:
-
分解数字:digits = [1, 9, 7]
-
初始和:1 + 9 + 7 = 17
-
生成数列:
17 → 17+9+7=33 → 9+7+17=33 → 7+17+33=57 → 17+33+57=107 → 33+57+107=197 ✅
可以看到197出现在数列中,验证通过。
性能优化
-
队列维护:仅保留最近n项,空间复杂度O(n)
-
实时和更新:每次计算新项时通过
currentSum += next - q.front()
避免重复求和 -
提前终止:一旦生成的值超过目标数立即返回
Java写法:
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
System.out.println(findMaxKevinNumber());
}
private static boolean isKevinNumber(int N) {
if (N == 0) return true;
String s = Integer.toString(N);
int n = s.length();
if (n == 1) return true; // 一位数总是符合要求
int[] digits = new int[n];
for (int i = 0; i < n; i++) {
digits[i] = s.charAt(i) - '0';
}
Deque<Integer> queue = new ArrayDeque<>(n);
for (int d : digits) {
queue.addLast(d);
}
int currentSum = sumDigits(digits);
if (currentSum == N) return true;
if (currentSum > N) return false;
while (true) {
int next = currentSum;
if (queue.size() >= n) {
currentSum -= queue.removeFirst();
}
queue.addLast(next);
currentSum += next;
if (next == N) return true;
if (next > N) return false;
}
}
private static int sumDigits(int[] arr) {
int sum = 0;
for (int num : arr) {
sum += num;
}
return sum;
}
public static int findMaxKevinNumber() {
// 从最大值开始向下搜索
for (int N = (int)1e7; N >= 0; N--) {
if (isKevinNumber(N)) {
return N;
}
}
return 0;
}
}
C++写法:
#include <iostream>
#include <string>
#include <deque>
using namespace std;
/**
* 判断一个数是否是类斐波那契循环数
* @param N 要检查的数字
* @return 如果是返回true,否则false
*/
bool isKevinNumber(int N) {
// 处理特殊情况:0 和一位数直接返回true
if (N == 0) return true;
string s = to_string(N);
int n = s.length();
if (n == 1) return true;
// 分解数字的每一位存入数组
deque<int> digits;
for (char c : s) {
digits.push_back(c - '0'); // '0'-'9'字符转换为数字
}
// 初始化和(前n项的和)
int currentSum = 0;
for (int d : digits) currentSum += d;
if (currentSum == N) return true; // 如果初始和等于N,直接符合
if (currentSum > N) return false; // 初始和已经超过N,不可能出现
// 维护一个固定长度为n的队列,存储最近的n项
deque<int> q = digits;
while (true) {
int next = currentSum; // 下一项是前n项的和
if (next == N) return true; // 找到目标数
if (next > N) return false; // 超过目标数,无需继续
// 更新队列:移除最旧项,添加新项
if (q.size() >= n) {
currentSum -= q.front(); // 减去即将移除的旧项
q.pop_front();
}
q.push_back(next);
currentSum += next; // 加上新项
}
}
/**
* 寻找0到1e7范围内最大的类斐波那契循环数
* @return 找到的最大符合条件的数
*/
int findMaxKevinNumber() {
// 从最大值开始向下搜索,找到第一个符合条件的立即返回
for (int N = 10000000; N >= 0; --N) {
if (isKevinNumber(N)) {
return N;
}
}
return 0; // 理论上不会执行到这里
}
int main() {
cout << findMaxKevinNumber() << endl;
return 0;
}
AC情况
时间复杂度和空间复杂度
复杂度分析
-
时间复杂度:O(1e7 * n),实际运行时远低于此(多数数会快速终止)
-
空间复杂度:O(n),用于存储队列
总结
wozhendehhhhhhhhhhhhhhhhhhhhhhhhhhhh