有基础班_day05课上题目
2025-07-05 16:17:22
发布于:上海
题目 |
---|
【递归】斐波那契数列 |
最大公约数 |
最小公倍数 |
进制转换1 |
进制转换2 |
进制转换3 |
前 n 项和 |
汉诺塔2 |
【递归】斐波那契数列
题目大意
给定一个正整数 (),请用递归方法求出斐波那契数列的第 项。
斐波那契数列定义如下:
解题思路
根据定义,我们可以用最朴素的递归方法实现。
对于输入的 :
- 如果 或 ,直接返回 ;
- 否则返回 。
虽然这个方法在效率上不是最优(时间复杂度是指数级的 ),但题目明确要求使用递归,并且 ,可以接受。
时间复杂度分析
- 时间复杂度:,因为会有大量重复递归调用;
- 空间复杂度:,递归深度最多为 。
代码实现(C++)
#include <iostream>
using namespace std;
// 最简单的递归定义
int fib(int n) {
if (n == 1 || n == 2) return 1;
return fib(n - 1) + fib(n - 2);
}
int main() {
int n;
cin >> n;
cout << fib(n) << endl;
return 0;
}
最大公约数
题目大意
给定两个正整数 和 ,要求输出它们的最大公约数(Greatest Common Divisor, GCD),即同时整除 和 的最大正整数。
题意分析
最大公约数是整数运算中非常基础的概念。可以使用辗转相除法(欧几里得算法)来高效计算:
- 当 时,结果为
例如:
解题思路
使用递归实现欧几里得算法,时间复杂度为 ,即使 最大为 ,效率也非常高。
时间复杂度分析
- 时间复杂度:
- 空间复杂度:
- 递归实现:(递归栈)
C++ 代码实现
#include <iostream>
using namespace std;
// 欧几里得算法求最大公约数
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
int main() {
int a, b;
cin >> a >> b;
cout << gcd(a, b) << endl;
return 0;
}
最小公倍数
题目大意
给定两个正整数 和 ,求它们的最小公倍数 ,即能同时被 和 整除的最小正整数。
题意分析
最小公倍数与最大公约数的关系如下:
其中 表示 和 的最大公约数。
举个例子:
- ,
- ,
解题思路
- 使用欧几里得算法计算 ;
- 使用公式 计算最小公倍数;
- 注意防止乘法时超出整数范围(不过本题数据保证结果在
int
范围内)。
时间复杂度分析
- 计算 :
- 总体复杂度:
C++ 代码实现
#include <iostream>
using namespace std;
// 欧几里得算法求最大公约数
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
// 最小公倍数
int lcm(int a, int b) {
return a / gcd(a, b) * b; // 先除再乘防止溢出
}
int main() {
int a, b;
cin >> a >> b;
cout << lcm(a, b) << endl;
return 0;
}
最小公倍数
题目大意
输入两个正整数 和 ,求它们的最小公倍数(LCM)。
题意分析
最小公倍数和最大公约数之间有如下关系:
解题思路
- 使用欧几里得算法计算 ;
- 再使用公式 ;
- 为防止整数溢出,使用
long long
存储结果。
时间复杂度分析
- 计算最大公约数:
- 计算最小公倍数:
总时间复杂度:
C++ 代码实现
#include <iostream>
using namespace std;
// 欧几里得算法计算最大公约数
long long gcd(long long a, long long b) {
return b == 0 ? a : gcd(b, a % b);
}
// 最小公倍数
long long lcm(long long a, long long b) {
return a / gcd(a, b) * b; // 先除再乘,避免中间结果溢出
}
int main() {
long long a, b;
cin >> a >> b;
cout << lcm(a, b) << endl;
return 0;
}
进制转换1
题目大意
将输入的一个十进制正整数 转换为对应的二进制数,并输出结果。要求必须使用递归完成。
题意分析
- 例如十进制数 ,它的二进制形式为 。
- 本质上是将 不断除以 ,从而构造出它的二进制表达。
- 输出顺序是从高位到低位,因此递归非常适合这个问题。
解题思路
我们考虑将十进制数 转换为二进制数的过程:
- 对 进行整数除以 得到下一层递归;
- 每次输出当前位的余数:;
- 递归的结束条件是: 时终止(但实际要从 才开始输出)。
为了防止前导 0 输出,我们在主程序中单独处理 的情况。
时间复杂度解析
- 每次递归都会将 除以 ,最多递归 层;
- 时间复杂度为 ,空间复杂度也是 ,非常高效。
代码演示
#include <iostream>
using namespace std;
// 递归函数:输出n的二进制表示
void printBinary(int n) {
if(n == 0) return; // 基础情况,递归终止
printBinary(n / 2); // 先递归处理高位
cout << n % 2; // 再输出当前位(低位)
}
int main() {
int n;
cin >> n;
if(n == 0) cout << 0; // 特判n = 0 的情况
else printBinary(n);
return 0;
}
进制转换2
题目大意
输入两个整数 和 ,将十进制整数 转换为 进制的数,并输出对应结果。要求必须使用递归实现。
题意分析
- 需要将 从十进制转换成 进制(其中 );
- 若 为 ,则要处理 对应的字符
A
~F
; - 输出顺序是从高位到低位,天然适合递归。
解题思路
递归函数思路如下:
- 若 ,终止递归;
- 否则先递归处理 ;
- 然后输出当前位:,若大于等于 ,输出
A
~F
。
另外在 main()
中要特殊处理 的情况(输出 0
)。
时间复杂度解析
- 每次递归将 除以 ,最多递归 次;
- 因此时间和空间复杂度均为 。
代码演示
#include <iostream>
using namespace std;
// 递归函数:将十进制数x转换为m进制
void convert(int x, int m) {
if(x == 0) return; // 递归终止条件
convert(x / m, m); // 先处理高位
int r = x % m; // 当前位
if(r < 10) cout << r;
else cout << char('A' + (r - 10)); // 10~15 映射到 A~F
}
int main() {
int x, m;
cin >> x >> m;
if(x == 0) cout << 0; // 特判x=0
else convert(x, m);
return 0;
}
进制转换3
题目大意
给定一个 N进制的数 (字符串形式) 和一个整数 ,将其转换为十进制数并输出。要求使用递归算法。
题意分析
- 输入中 是一个 字符串,可能包含数字
0
~9
和字母A
~F
或a
~f
(表示 10~15); - 进制 范围在 ;
- 要将字符串 解释为 进制,转化成十进制整数;
- 用递归实现:自然可以从左到右依次处理每一位。
解题思路
我们可以定义一个递归函数如下:
- 定义:
convert(s, len, base)
表示将前len
位的字符串s
视为 base 进制数,转换为十进制。 - 递归表达式:
- 若长度为 1:直接返回该位的十进制值;
- 否则,答案为:
convert(s, len-1, base) * base + 当前位的十进制值
。
时间复杂度
- 每一位递归一次,共有 位;
- 时间复杂度为 ,最多约为 ,很快。
代码演示
#include <iostream>
#include <string>
using namespace std;
// 将字符 ch 转成它的十进制值
int toDigit(char ch) {
if(ch >= '0' && ch <= '9') return ch - '0';
if(ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
if(ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
return -1; // 非法字符
}
// 递归函数:将前len位的s视为base进制,转为十进制
int convert(string s, int len, int base) {
if(len == 1) return toDigit(s[0]);
return convert(s, len - 1, base) * base + toDigit(s[len - 1]);
}
int main() {
string s;
int base;
cin >> s >> base;
cout << convert(s, s.length(), base) << endl;
return 0;
}
前 n 项和
题目大意
输入一个正整数 ,使用递归的方式求出 的结果。
题意分析
- 题目要求使用递归方法求和;
- 不能使用循环或数学公式 。
解题思路
使用递归公式定义:
- 定义
f(n)
为前 项和; - 递推关系:
- 边界:
时间复杂度分析
- 每次递归调用 次,时间复杂度为 ;
- 空间复杂度(递归栈)也是 ,对于 完全可以接受。
代码演示
#include <iostream>
using namespace std;
// 递归函数:返回1+2+...+n的和
int sum(int n) {
if (n == 1) return 1; // 递归终止条件
return sum(n - 1) + n; // 递归表达式
}
int main() {
int n;
cin >> n;
cout << sum(n) << endl;
return 0;
}
汉诺塔2
题目大意
有 种大小不同的圆盘,每种大小各有两个相同的圆盘(共 个),初始都在柱子 A 上。按照 汉诺塔规则(一次只能移动一个圆盘、且大盘不能放在小盘上),将所有盘子移动到柱子 C 上,输出最少移动次数。
注意:同一尺寸的两个圆盘是不可区分的。
题意分析
- 和普通汉诺塔不同的是:每种尺寸有 两个不可区分的圆盘。
- 目标是移动这 个圆盘到柱 C,最少需要多少步?
解题思路
观察样例和递归特性,我们可以发现:
普通汉诺塔的递归步骤(n 个不同盘):移动 个盘子的最少步数为 。
本题特殊之处:每个盘有两个一模一样的拷贝,但规则仍然要求不能把大盘放在小盘上。
构造递推公式:
设 表示将 种盘移动到目标柱的最小步数,则:
- 首先把 对盘移到辅助柱子,需要 步;
- 然后将当前第 对盘子从起始柱移到目标柱,共 2 步;
- 然后再将辅助柱上的 对盘子移到目标柱,再需要 步。
于是可以得出递推式:
- 边界条件:
时间复杂度分析
这是一个递归式,时间复杂度为 ,不会爆栈或超时, 时可以安全运行。
代码演示
#include <iostream>
using namespace std;
// 递归函数:求解 n 种圆盘最少移动次数
long long f(int n) {
if (n == 1) return 2; // 基础情况:1 对盘需要 2 次
return 2 * f(n - 1) + 2; // 递推公式
}
int main() {
int n;
cin >> n;
cout << f(n) << endl; // 输出最少移动次数
return 0;
}
全部评论 1
`#include <iostream> using namespace std; // 递归函数:求解 n 种圆盘最少移动次数 long long f(int n) { if (n == 1) return 2; // 基础情况:1 对盘需要 2 次 return 2 * f(n - 1) + 2; // 递推公式 } int main() { int n; cin >> n; cout << f(n) << endl; // 输出最少移动次数 return 0; }`
2025-07-06 来自 上海
0
有帮助,赞一个