题目 【递归】斐波那契数列 最大公约数 最小公倍数 进制转换1 进制转换2 进制转换3 前 n 项和 汉诺塔2
【递归】斐波那契数列
题目大意
给定一个正整数 nnn(1≤n≤301 \leq n \leq 301≤n≤30),请用递归方法求出斐波那契数列的第 nnn 项。
斐波那契数列定义如下:
F(1)=1,F(2)=1,F(n)=F(n−1)+F(n−2)(n≥3)F(1) = 1,\quad F(2) = 1,\quad F(n) = F(n-1) + F(n-2)\quad (n \geq 3)F(1)=1,F(2)=1,F(n)=F(n−1)+F(n−2)(n≥3)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
根据定义,我们可以用最朴素的递归方法实现。
对于输入的 nnn:
* 如果 n=1n = 1n=1 或 n=2n = 2n=2,直接返回 111;
* 否则返回 f(n−1)+f(n−2)f(n - 1) + f(n - 2)f(n−1)+f(n−2)。
虽然这个方法在效率上不是最优(时间复杂度是指数级的 O(2n)O(2^n)O(2n)),但题目明确要求使用递归,并且 n≤30n \leq 30n≤30,可以接受。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
* 时间复杂度:O(2n)O(2^n)O(2n),因为会有大量重复递归调用;
* 空间复杂度:O(n)O(n)O(n),递归深度最多为 nnn。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码实现(C++)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最大公约数
题目大意
给定两个正整数 aaa 和 bbb,要求输出它们的最大公约数(Greatest Common Divisor, GCD),即同时整除 aaa 和 bbb 的最大正整数。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
最大公约数是整数运算中非常基础的概念。可以使用辗转相除法(欧几里得算法)来高效计算:
* gcd(a,b)=gcd(b,a mod b)\gcd(a, b) = \gcd(b, a \bmod b)gcd(a,b)=gcd(b,amodb)
* 当 b=0b = 0b=0 时,结果为 aaa
例如:
* gcd(16,6)→gcd(6,4)→gcd(4,2)→gcd(2,0)=2\gcd(16, 6) \to \gcd(6, 4) \to \gcd(4, 2) \to \gcd(2, 0) = 2gcd(16,6)→gcd(6,4)→gcd(4,2)→gcd(2,0)=2
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
使用递归实现欧几里得算法,时间复杂度为 O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b)),即使 a,ba, ba,b 最大为 10510^5105,效率也非常高。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
* 时间复杂度:O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b))
* 空间复杂度:
* 递归实现:O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b))(递归栈)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
C++ 代码实现
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最小公倍数
题目大意
给定两个正整数 aaa 和 bbb,求它们的最小公倍数 LCM(a,b)LCM(a, b)LCM(a,b),即能同时被 aaa 和 bbb 整除的最小正整数。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
最小公倍数与最大公约数的关系如下:
LCM(a,b)=a×bgcd(a,b)\mathrm{LCM}(a, b) = \frac{a \times b}{\gcd(a, b)}LCM(a,b)=gcd(a,b)a×b
其中 gcd(a,b)\gcd(a, b)gcd(a,b) 表示 aaa 和 bbb 的最大公约数。
举个例子:
* gcd(3,4)=1\gcd(3, 4) = 1gcd(3,4)=1,LCM(3,4)=3×41=12\mathrm{LCM}(3, 4) = \frac{3 \times 4}{1} = 12LCM(3,4)=13×4 =12
* gcd(2,6)=2\gcd(2, 6) = 2gcd(2,6)=2,LCM(2,6)=2×62=6\mathrm{LCM}(2, 6) = \frac{2 \times 6}{2} = 6LCM(2,6)=22×6 =6
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
1. 使用欧几里得算法计算 gcd(a,b)\gcd(a, b)gcd(a,b);
2. 使用公式 LCM(a,b)=a⋅bgcd(a,b)\mathrm{LCM}(a, b) = \dfrac{a \cdot b}{\gcd(a, b)}LCM(a,b)=gcd(a,b)a⋅b 计算最小公倍数;
3. 注意防止乘法时超出整数范围(不过本题数据保证结果在 int 范围内)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
* 计算 gcd\gcdgcd:O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b))
* 总体复杂度:O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b))
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
C++ 代码实现
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最小公倍数
题目大意
输入两个正整数 aaa 和 bbb,求它们的最小公倍数(LCM)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
最小公倍数和最大公约数之间有如下关系:
LCM(a,b)=a×bgcd(a,b)\mathrm{LCM}(a, b) = \frac{a \times b}{\gcd(a, b)}LCM(a,b)=gcd(a,b)a×b
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
1. 使用欧几里得算法计算 gcd(a,b)\gcd(a, b)gcd(a,b);
2. 再使用公式 LCM=a⋅bgcd(a,b)\mathrm{LCM} = \dfrac{a \cdot b}{\gcd(a, b)}LCM=gcd(a,b)a⋅b ;
3. 为防止整数溢出,使用 long long 存储结果。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
* 计算最大公约数:O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b))
* 计算最小公倍数:O(1)O(1)O(1)
总时间复杂度:O(logmin(a,b))O(\log \min(a, b))O(logmin(a,b))
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
C++ 代码实现
进制转换1
题目大意
将输入的一个十进制正整数 nnn 转换为对应的二进制数,并输出结果。要求必须使用递归完成。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
* 例如十进制数 666,它的二进制形式为 110110110。
* 本质上是将 nnn 不断除以 222,从而构造出它的二进制表达。
* 输出顺序是从高位到低位,因此递归非常适合这个问题。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
我们考虑将十进制数 nnn 转换为二进制数的过程:
1. 对 nnn 进行整数除以 222 得到下一层递归;
2. 每次输出当前位的余数:nn % 2n;
3. 递归的结束条件是:n==0n == 0n==0 时终止(但实际要从 n>0n > 0n>0 才开始输出)。
为了防止前导 0 输出,我们在主程序中单独处理 n=0n=0n=0 的情况。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度解析
* 每次递归都会将 nnn 除以 222,最多递归 log2n\log_2 nlog2 n 层;
* 时间复杂度为 O(logn)O(\log n)O(logn),空间复杂度也是 O(logn)O(\log n)O(logn),非常高效。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
进制转换2
题目大意
输入两个整数 XXX 和 MMM,将十进制整数 XXX 转换为 MMM 进制的数,并输出对应结果。要求必须使用递归实现。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
* 需要将 XXX 从十进制转换成 MMM 进制(其中 2≤M≤162 \leq M \leq 162≤M≤16);
* 若 MMM 为 161616,则要处理 10∼1510\sim1510∼15 对应的字符 A~F;
* 输出顺序是从高位到低位,天然适合递归。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
递归函数思路如下:
1. 若 X==0X == 0X==0,终止递归;
2. 否则先递归处理 X/MX / MX/M;
3. 然后输出当前位:XX % MX,若大于等于 101010,输出 A~F。
另外在 main() 中要特殊处理 X=0X = 0X=0 的情况(输出 0)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度解析
* 每次递归将 XXX 除以 MMM,最多递归 logMX\log_M XlogM X 次;
* 因此时间和空间复杂度均为 O(logX)O(\log X)O(logX)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
进制转换3
题目大意
给定一个 N进制的数 XXX(字符串形式) 和一个整数 NNN,将其转换为十进制数并输出。要求使用递归算法。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
* 输入中 XXX 是一个 字符串,可能包含数字 0~9 和字母 A~F 或 a~f(表示 10~15);
* 进制 NNN 范围在 2∼162 \sim 162∼16;
* 要将字符串 XXX 解释为 NNN 进制,转化成十进制整数;
* 用递归实现:自然可以从左到右依次处理每一位。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
我们可以定义一个递归函数如下:
* 定义:convert(s, len, base) 表示将前 len 位的字符串 s 视为 base 进制数,转换为十进制。
* 递归表达式:
* 若长度为 1:直接返回该位的十进制值;
* 否则,答案为:convert(s, len-1, base) * base + 当前位的十进制值。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度
* 每一位递归一次,共有 len(X)\text{len}(X)len(X) 位;
* 时间复杂度为 O(len(X))O(\text{len}(X))O(len(X)),最多约为 O(7)O(7)O(7),很快。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
前 N 项和
题目大意
输入一个正整数 nnn,使用递归的方式求出 1+2+3+⋯+n1 + 2 + 3 + \cdots + n1+2+3+⋯+n 的结果。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
* 题目要求使用递归方法求和;
* 不能使用循环或数学公式 n(n+1)/2n(n+1)/2n(n+1)/2。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
使用递归公式定义:
* 定义 f(n) 为前 nnn 项和;
* 递推关系:
* f(n)=f(n−1)+nf(n) = f(n - 1) + nf(n)=f(n−1)+n
* 边界:f(1)=1f(1) = 1f(1)=1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
* 每次递归调用 n−1n-1n−1 次,时间复杂度为 O(n)O(n)O(n);
* 空间复杂度(递归栈)也是 O(n)O(n)O(n),对于 n≤100n \leq 100n≤100 完全可以接受。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
汉诺塔2
题目大意
有 nnn 种大小不同的圆盘,每种大小各有两个相同的圆盘(共 2n2n2n 个),初始都在柱子 A 上。按照 汉诺塔规则(一次只能移动一个圆盘、且大盘不能放在小盘上),将所有盘子移动到柱子 C 上,输出最少移动次数。
注意:同一尺寸的两个圆盘是不可区分的。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
* 和普通汉诺塔不同的是:每种尺寸有 两个不可区分的圆盘。
* 目标是移动这 2n2n2n 个圆盘到柱 C,最少需要多少步?
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
观察样例和递归特性,我们可以发现:
普通汉诺塔的递归步骤(n 个不同盘):移动 nnn 个盘子的最少步数为 2n−12^n - 12n−1。
本题特殊之处:每个盘有两个一模一样的拷贝,但规则仍然要求不能把大盘放在小盘上。
构造递推公式:
设 f(n)f(n)f(n) 表示将 nnn 种盘移动到目标柱的最小步数,则:
* 首先把 n−1n-1n−1 对盘移到辅助柱子,需要 f(n−1)f(n-1)f(n−1) 步;
* 然后将当前第 nnn 对盘子从起始柱移到目标柱,共 2 步;
* 然后再将辅助柱上的 n−1n-1n−1 对盘子移到目标柱,再需要 f(n−1)f(n-1)f(n−1) 步。
于是可以得出递推式:
f(n)=2×f(n−1)+2f(n) = 2 \times f(n-1) + 2f(n)=2×f(n−1)+2
* 边界条件:f(1)=2f(1) = 2f(1)=2
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
这是一个递归式,时间复杂度为 O(n)O(n)O(n),不会爆栈或超时,n≤15n \leq 15n≤15 时可以安全运行。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示