蓝桥杯真题——类斐波那契循环数

目录

题目链接:1.类斐波那契循环数 - 蓝桥云课

题目描述

解法一:

1. 问题核心理解

2. 代码核心逻辑

isKevinNumber 函数

findMaxKevinNumber 函数

示例验证

性能优化

Java写法:

C++写法:

AC情况

时间复杂度和空间复杂度

复杂度分析

总结


题目链接:1.类斐波那契循环数 - 蓝桥云课

注:下述题目描述和示例均来自蓝桥云客

题目描述

对于一个有 n 位的十进制数 N = d1d2d3;...dn,可以生成一个类斐波那契数列S,数列 S 的前 n 个数为:

{S1=d1,S2=d2,S3 = d3,...,Sn= dn}

数列 S 的第 k(k > n)个数为:

\sum_{i=k-n}^{k-1} Si

如果这个数 N 会出现在对应的类斐波那契数列 S 中,那么 N 就是一个类斐波那契循环数。
例如对于 197,对应的数列 S 为:

{1,9,7,17,33,57,107,197,...}

197 出现在 S 中,所以 197 是一个类斐波那契循环数。
请问在 0 至 10^7中,最大的类裴波那契循环数是多少?


解法一:

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 函数
  1. 特殊处理

    • 0 和一位数直接返回true(根据定义)

    if (N == 0) return true;
    if (n == 1) return true;

  2. 数字分解

    • 将数字转为字符串,逐字符提取每一位

    string s = to_string(N);
    for (char c : s) digits.push_back(c - '0');

  3. 初始化和校验

    • 计算初始和(前n项的和)

    • 如果初始和直接等于或超过N,提前返回

    int currentSum = sum(digits);
    if (currentSum == N) return true;
    if (currentSum > N) return false;

  4. 动态生成数列

    • 使用双端队列 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 为例:

  1. 分解数字:digits = [1, 9, 7]

  2. 初始和:1 + 9 + 7 = 17

  3. 生成数列

    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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值