文章目录
一、意义
1. 举例
经典年龄游戏
问第五个多少岁,他说比第四个大两岁。
问第四个多少岁,他说比第三个大两岁。
问第三个多少岁,他说比第二个大两岁。
问第二个多少岁,他说比第一个大两岁。
问第一个多少岁,他说10岁了。
问:第五个人的年龄?
递归样式
a g e ( 5 ) = a g e ( 4 ) + 2 age(5)=age(4)+2 age(5)=age(4)+2
a g e ( 4 ) = a g e ( 3 ) + 2 age(4)=age(3)+2 age(4)=age(3)+2
a g e ( 3 ) = a g e ( 2 ) + 2 age(3)=age(2)+2 age(3)=age(2)+2
a g e ( 2 ) = a g e ( 1 ) + 2 age(2)=age(1)+2 age(2)=age(1)+2
a g e ( 1 ) = 10 age(1)=10 age(1)=10
a g e ( 2 ) = 10 + 2 = 12 age(2)=10+2=12 age(2)=10+2=12
a g e ( 3 ) = 12 + 2 = 14 age(3)=12+2=14 age(3)=12+2=14
a g e ( 4 ) = 14 + 2 = 16 age(4)=14+2=16 age(4)=14+2=16
a g e ( 5 ) = 16 + 2 = age(5)=16+2= age(5)=16+2= 18 18 18
递归要素
公式: a g e ( n ) = a g e ( n − 1 ) + 2 age(n)=age(n-1)+2 age(n)=age(n−1)+2
边界:第一个人的年龄
2. 意义
递归就是函数自己调用自己。比如上面的游戏可以用下面的程序来实现:
int age(int n) { if (n == 1) return 10; // 边界 return age(n-1) + 2; // 公式 }
而
return
的内容就会这样执行:
当 n = 5 n=5 n=5,
a g e ( 5 ) = a g e ( 4 ) + 2 age(5)=age(4)+2 age(5)=age(4)+2
a g e ( 4 ) = a g e ( 3 ) + 2 age(4)=age(3)+2 age(4)=age(3)+2
a g e ( 3 ) = a g e ( 2 ) + 2 age(3)=age(2)+2 age(3)=age(2)+2
a g e ( 2 ) = a g e ( 1 ) + 2 age(2)=age(1)+2 age(2)=age(1)+2
a g e ( 1 ) = 10 age(1)=10 age(1)=10
a g e ( 2 ) = 10 + 2 = 12 age(2)=10+2=12 age(2)=10+2=12
a g e ( 3 ) = 12 + 2 = 14 age(3)=12+2=14 age(3)=12+2=14
a g e ( 4 ) = 14 + 2 = 16 age(4)=14+2=16 age(4)=14+2=16
a g e ( 5 ) = 16 + 2 = age(5)=16+2= age(5)=16+2= 18 18 18
二、队尾身高
1. 审题
题目描述
班级里有 n n n 个人按照从高到低进行排队,班主任让你去问下队尾的人身高是多少,队尾的人说,我的身高是前一个人身高的 90 % 90\% 90%,问第 n − 1 n-1 n−1 个人身高,他说我的身高是第 n − 2 n-2 n−2 个人身高的 90 % 90\% 90%,依次类推,你一直问到了队首,队首说我刚好 200 c m 200cm 200cm。问队尾的人身高是多少?
输入描述
输入一行一个整数 n n n,表示排队的总人数
输出描述
输出一行一个数据,表示队尾这个人的身高( c m cm cm)
样例1
输入
2
输出
180
提示
对于 100 % 100\% 100% 的数据 0 ≤ n < 15 0\le n<15 0≤n<15
2. 思路
函数:找第 n n n 个人的身高
公式: h ( n ) = h ( n − 1 ) × 0.9 h(n)=h(n-1)\times0.9 h(n)=h(n−1)×0.9
边界: n = = 1 n==1 n==1
h ( n ) = { n = = 1 2 00 n > 1 h ( n ) = h ( n − 1 ) × 90 % h(n) = \begin{cases} n==1 & \text 200 \\ n>1 & \text h(n)=h(n-1)\times90\% \end{cases} h(n)={n==1n>1200h(n)=h(n−1)×90%
3. 参考答案
#include <iostream>
using namespace std;
int n;
double h(int n)
{
if (n == 1) return 200;
return h(n-1) * 0.9;
}
int main()
{
cin >> n;
if (n == 0)
{
cout << "Error: It is not possible to have 0 people.";
return 0;
}
cout << h(n);
return 0;
}
三、斐波那契数列
1. 思路
暴力枚举,时间复杂度接近 O ( 2 n ) O(2^n) O(2n)
函数:找第 n n n 个人的身高
公式: f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2)
边界: n = = 1 n==1 n==1 和 n = = 2 n==2 n==2
f ( n ) = { n = = 1 1 n = = 2 1 n > 2 f ( n − 1 ) + f ( n − 2 ) f(n) = \begin{cases} n==1& \text 1 \\ n==2& \text 1 \\ n>2 & \text f(n-1) + f(n-2) \end{cases} f(n)=⎩ ⎨ ⎧n==1n==2n>211f(n−1)+f(n−2)
2. 参考答案
#include <iostream>
using namespace std;
int n;
long long f(int n)
{
if (n <= 2) return 1;
return f(n-1) + f(n-2);
}
int main()
{
cin >> n;
if (n == 0)
{
cout << "impossible index.";
}
cout << f(n);
return 0;
}
四、楼梯走法
1. 审题
题目描述
课间你和同学们玩的大汗淋漓,此时上课铃声突然响了,你快速飞奔到教学楼下,你所在的班级在高楼层,你因为课间玩耍消耗了太多的力气,在上楼时,你有时候一步一个台阶,有时候一步两个台阶,有时候一步三个台阶,到班级里的你突然想到自己刚才爬楼梯的场景,你突发奇想,想要计算下到第 n n n 个台阶总共有多少种走法?请你编写递归程序,实现这个问题吧。
输入描述
输入一个正整数 n n n,表示可多到第 n n n 个台阶
输出描述
输出一个整数,表示可多到达第n个台阶总共有多少种走法
样例1
输入
1
输出
1
样例2
输入
2
输出
2
样例3
输入
3
输出
4
提示
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 35 1\le n\le 35 1≤n≤35
2. 思路
方法1:递归
f ( 1 ) = 1 f(1)=1 f(1)=1
f ( 2 ) = 2 f(2)=2 f(2)=2
f ( 3 ) = 4 f(3)=4 f(3)=4
f ( 4 ) = 7 f(4)=7 f(4)=7
f ( 5 ) = 13 f(5)=13 f(5)=13
函数:找 n n n 个台阶的所有可能数
公式: f ( n ) = f ( n − 1 ) + f ( n − 2 ) + f ( n − 3 ) f(n)=f(n-1)+f(n-2)+f(n-3) f(n)=f(n−1)+f(n−2)+f(n−3)
边界: n = = 1 n==1 n==1 和 n = = 2 n==2 n==2 和 n = = 3 n==3 n==3
f ( n ) = { n = = 1 1 n = = 2 2 n = = 3 4 n > 3 f ( n − 1 ) + f ( n − 2 ) + f ( n − 3 ) f(n) = \begin{cases} n==1& \text 1 \\ n==2& \text 2 \\ n==3& \text 4 \\ n>3 & \text f(n-1) + f(n-2) + f(n-3) \end{cases} f(n)=⎩ ⎨ ⎧n==1n==2n==3n>3124f(n−1)+f(n−2)+f(n−3)
方法2:简单记忆化搜索
使用一个数组,将答案存储到记忆中。
3. 参考答案
3.1 递归
#include <iostream>
using namespace std;
int n;
long long f(int n)
{
if (n == 1) return 1;
if (n == 2) return 2;
if (n == 3) return 4;
return f(n-1) + f(n-2) + f(n-3);
}
int main()
{
cin >> n;
cout << f(n);
return 0;
}
3.2 记忆化搜索
#include <iostream>
using namespace std;
int n;
int a[40] = {0, 1, 2, 4};
long long f(int n)
{
if (a[n] != 0) return a[n];
if (n == 1) return 1;
if (n == 2) return 2;
if (n == 3) return 4;
return a[n] = f(n-1) + f(n-2) + f(n-3);
}
int main()
{
cin >> n;
cout << f(n);
return 0;
}
四、辗转相除
1. 审题
我们知道辗转相除法,最基础的代码是:
while (a % b != 0) { r = a % b; a = b; b = r; } cout << b;
那么,应该如何用递归算法实现呢?
2. 参考答案
#include <iostream>
using namespace std;
long long a, b;
int gcd(int x, int y)
{
if (x % y == 0) return y;
return gcd(y, x % y);
}
int main()
{
cin >> a >> b;
int gcdAns = gcd(a, b);
long long lcmAns = a * b / gcdAns;
// cout << lcmAns;
// cout << gcdAns;
return 0;
}
五、角谷猜想/克拉兹猜想/冰雹猜想步数
1. n 到 1 经过的次数
#include <iostream>
using namespace std;
int n;
int f(int n)
{
if (n == 1) return 0;
if (n % 2 == 0) return f(n/2) + 1;
else return f(n*3+1) + 1;
}
int main()
{
cin >> n;
cout << f(n);
return 0;
}
2. 找 n 的下一个数
#include <iostream>
using namespace std;
int n;
int cnt;
void f(int n)
{
if (n == 1) return;
cnt++;
if (n % 2 == 0) f(n/2);
else f(n*3+1);
}
int main()
{
cin >> n;
f(n);
cout << cnt;
return 0;
}
3. 累加次数
#include <iostream>
using namespace std;
int f(int n, int cnt)
{
if (n == 1) return cnt;
if (n % 2 == 0) return f(n/2, cnt+1);
else return f(n*3+1, cnt+1);
}
int main()
{
int n;
int cnt = 0;
cin >> n;
cnt = f(n, 0);
cout << cnt;
return 0;
}
六、[NOIP2001 普及组] 数的计算
1. 审题
题目描述
给出正整数 n n n,要求按如下方式构造数列:
- 只有一个数字 n n n 的数列是一个合法的数列。
- 在一个合法的数列的末尾加入一个正整数,但是这个正整数不能超过该数列最后一项的一半,可以得到一个新的合法数列。
请你求出,一共有多少个合法的数列。两个合法数列 a , b a, b a,b 不同当且仅当两数列长度不同或存在一个正整数 i ≤ ∣ a ∣ i \leq |a| i≤∣a∣,使得 a i ≠ b i a_i \neq b_i ai=bi。
输入格式
输入只有一行一个整数,表示 n n n。
输出格式
输出一行一个整数,表示合法的数列个数。
样例 #1
样例输入 #1
6
样例输出 #1
6
提示
样例 1 解释
满足条件的数列为:
- 6 6 6
- 6 , 1 6, 1 6,1
- 6 , 2 6, 2 6,2
- 6 , 3 6, 3 6,3
- 6 , 2 , 1 6, 2, 1 6,2,1
- 6 , 3 , 1 6, 3, 1 6,3,1
数据规模与约定
对于全部的测试点,保证 1 ≤ n ≤ 1 0 3 1 \leq n \leq 10^3 1≤n≤103。
2. 参考答案
2.1 简单递归
#include <iostream>
using namespace std;
int n;
int cnt;
void f(int x)
{
if (x == 1) return; // 一个分叉的边界
for (int i = 1; i <= x/2; i++)
{
cnt++; // 记录调用了几次f()函数
f(i);
}
}
int main()
{
cin >> n;
f(n);
cout << cnt+1;
return 0;
}