洛谷【C++编程基础】递归函数初步 专题解题报告
T1-T89304 递归求和
题目描述
用递归的方法求1+2+3+4+…+(n-1)+n的值。
输入格式
一个整数n。(1<=n<=10000).
输出格式
一个整数,数列的和。
输入输出样例
输入
10
输出
55
题目出处
题目要求:用递归的方法求1+2+3+4+…+(n-1)+n的值。
#include <bits/stdc++.h>
using namespace std;
int f(int a){
if(a==1)return 1;//边界
return (a+f(a-1));//递归关系式
}
int main(){
int n;
cin>>n;
cout<<f(n)<<endl;
return 0;
}
T2-T89307 Hermite多项式
题目描述
用递归的方法求Hermite多项式的值
对给定的实数x和正整数n,求多项式值。
中间的Hermite图片
输入格式
两个数x,n。用空格隔开。(-1<x<1,1<=n<=20)
输出格式
一个数,函数值。(保留两位小数)
输入输出样例
输入
-0.10 1
输出
-0.20
题目出处
递归关系式已经给出来了,照着写呗。
#include <bits/stdc++.h>
using namespace std;
double n,x;//注意数据类型
double f(double n,double x){
if(n==0)return 1;
if(n==1)return 2*x;
return 2*x*f(n-1,x)-2*(n-1)*f(n-2,x);//照着图片写呗
}
int main(){
cin>>x>>n;
cout<<fixed<<setprecision(2)<<f(n,x)<<endl;//题目要求保留两位小数
return 0;
}
T3-T89310 递归函数求值1
题目描述
已知图片
用递归方法求解。
输入格式
一行有两个整数x和n,用空格隔开。(1<x<30000,1<=n<=10000)
输出格式
一个实数,即函数值。(保留两位小数)
输入输出样例
输入
24499 8564
输出
2.86
题目出处
这一题比上一题略麻烦一点儿,要自己分析。
递归关系:f(x,n)=[n+f(n-1)]/x
边界:n=1时 return (1+x)/x
(或:当n=0时return x)
#include <bits/stdc++.h>
using namespace std;
int n,x;
double f(double x,double n){
if(n==1)return x/(1+x);
return x/(n+f(x,n-1));
}
int main(){
cin>>x>>n;
cout<<fixed<<setprecision(2)<<f(x,n);
return 0;
}
T4-T89314 递归函数求值2
题目描述
已知
图片
根据x,n的值计算函数值。
输入格式
用空格隔开的两个数,实数x(0<x<=100),整数n(0<n<=10000).
输出格式
函数值。保留两位小数。
输入输出样例
输入
4.2 10
输出
3.68
题目出处
递归关系:f(x,n)=sqrt(n+f(x,n-1))
边界:当n=1时return sqrt(1+x)
(或:当n=0时return x)
#include <bits/stdc++.h>
using namespace std;
float n,x;
double f(double x,double n){
if(n==1)return sqrt(1+x);
return sqrt(n+f(x,n-1));
}
int main(){
cin>>x>>n;
cout<<fixed<<setprecision(2)<<f(x,n);
return 0;
}
T5-T89316 汉诺塔问题
题目描述
如图,设有n个大小不等的中空圆盘,按照从小到大的顺序迭套在立柱A上,另有两根立柱B和C。现要求把全部圆盘从A柱(源柱)移到C柱(目标柱),移动过程中可借助B柱(中间柱)。移动时有如下的要求:
1、一次只许移动一个盘。
2、任何时候任何柱子上不允许把大盘放在小盘上边。
3、可使用任意一根立柱暂存圆盘。
问:如何用最少步数实现n个盘子的移动,请打印出方案。
输入格式
一个整数n(3<=n<=16)
输出格式
输出移动最少的方案,每行表示一次移动,如A->B表示将A柱上最上面的圆盘移动到B柱上。
最后一行输出最少的步数。
图片
输入输出样例
输入
3
输出
A->C
A->B
C->B
A->C
B->A
B->C
A->C
7
其实,可以将移动n个圆盘到目标柱理解为将(n-1)个圆盘移动到中间柱+移动第n个到目标柱+将(n-1)个圆盘移动到目标柱
也就是(递归关系式)
hanot(n-1,a,c,b);//(n-1)个圆盘移动到中间柱
cout<<a<<"->"<<c<<endl;//移动第n个到目标柱
hanot(n-1,b,a,c);//将(n-1)个圆盘移动到目标柱
边界
if(n==1)将这根柱子移动到目标柱
由于题目要步数,就设成了有返回值的(也可以定义全局变量替代)
#include <bits/stdc++.h>
using namespace std;
int n;
int hanot(int n,char a,char b,char c){//a目标柱,b中间柱,c目标柱
if(n==1){
cout<<a<<"->"<<c<<endl;
return 1;
}
int ans=1;
ans+=hanot(n-1,a,c,b);
cout<<a<<"->"<<c<<endl;
ans+=hanot(n-1,b,a,c);
return ans;
}
int main(){
cin>>n;
cout<<hanot(n,'A','B','C')<<endl;
return 0;
}
T6-T90615 字符串逆序
题目描述
输入一串以‘!’结束的字符,按逆序输出。(请用递归完成)
输入格式
一行字符串(以!结束)(字符串长度不超过100)
输出格式
逆序输出。
输入输出样例
输入
abc!
输出
!cba
题目出处
这是我一开始的程序:
#include <bits/stdc++.h>
using namespace std;
string s;
string f(string s){
int pos=s.size();
if(pos==1)return s;
char er=s[pos-1];
string st;
for(int i=0;i<pos-1;++i)
st+=s[i];
return er+f(st);
}
int main(){
cin>>s;
cout<<f(s)<<endl;
return 0;
}
现在发现太繁琐:
#include <bits/stdc++.h>
using namespace std;
string s;
int pos;//全局变量,以便用于递归(当然,可以通过传参代替)
void f(int n){
if(n<pos-1)f(n+1);//边界
cout<<s[n];//逆序输出(很像出栈序列,嘿嘿)
}
int main(){
cin>>s;
pos=s.size();
f(0);
cout<<endl;
return 0;
}
呸呸,更正!!!
测试数据太弱了,竟然没测出BUG!!!
(将
cin>>s;
改为
getline(cin,s);
)
当然,还可以这样做:
#include <bits/stdc++.h>
using namespace std;
int pos;
void f(){
char fox;
cin>>fox;
if(fox=='!'){
cout<<"!";
return;
}//边界
f();
cout<<fox;
}
int main(){
f();
cout<<endl;
return 0;
}
但注意!!!这个的BUG也是不能输入空格。将
cin>>fox;
改为
fox=getchar();
即可。
T7-T90627 费波那契数列
1,1,2,3,5,8,13,…
这是著名的斐波那契(Leonardo Pisano ,Fibonacci, Leonardo Bigollo,1175-1250)发现的数列。其中,每两项都等于前二项之和(第1,2项是1)。
题目描述
1,1,2,3,5,8,13,21,34,55……从第三项起,每一项都是紧挨着的前两项的和。
以上就是斐波那契数列。
输入第几项,输出第几项的值。(请用递归完成)
输入格式
一个正整数n,<=30
输出格式
第n项的值
输入输出样例
输入
4
输出
3
题目出处
递归关系:f(n)=f(n-1)+f(n-2)
边界:当n==1或2时return 1
代码
#include <bits/stdc++.h>
using namespace std;
long long n,dp[110];//dp为记忆化数组,以空间换时间(当然本题数据较小,可以不用记忆化,并且可以不开long long)
long long f(long long n){
if(n<=2){
return 1;
}//边界
if(dp[n])return dp[n];
dp[n]=f(n-1)+f(n-2);
return dp[n];
}
int main(){
cin>>n;
cout<<f(n)<<endl;
return 0;
}
当然,还得展示一下我个人的奇葩代码,思路奇怪。(简直不能说是递归)像函数实现for循环
#include <bits/stdc++.h>
using namespace std;
int n,s=2;
int f(int a,int b){
++s;
int c=a+b;
if(s==n)return c;
return f(b,c);
}
int main(){
cin>>n;
if(n==1||n==2)cout<<1<<endl;
else cout<<f(1,1)<<endl;
return 0;
}
当然,再展示一下for循环代码
#include <bits/stdc++.h>
using namespace std;
long long n,dp[110];
int main(){
cin>>n;
dp[1]=dp[2]=1;
for(int i=3;i<=n;++i)dp[i]=dp[i-1]+dp[i-2];
cout<<dp[n]<<endl;
return 0;
}
T8-T90628 F91
题目描述
麦卡锡是一个有名的计算机科学专家,在他的著作中,他定义了一个被称为"F91"的递归函数,这个函数是这样获得的:输入一个正整数N,按如下定义返回一个正整数:
If N ≤ 100, then f91(N) = f91(f91(N+11));
If N ≥ 101, then f91(N) = N-10
编写程序计算出麦卡锡的F91函数
输入格式
输入数据包含一系列的正整数,每个正整数最大不超过1,000,000,每个正整数占一行,当遇到数字0时表示输入结束。注意数字0不是测试数据,仅代表结束标志。
输出格式
输出数据每行应包含一个测试结果,具体格式见输出样例,等号两侧各有一个空格。
输入输出样例
输入
91
622
1997
0
输出
f91(91) = 91
f91(622) = 612
f91(1997) = 1987
说明/提示
输入数据保证你用直接递归不会超时。
题目出处
既然题目给出了递归关系式,并保证“直接递归不会超时”,那就写呗。
#include <bits/stdc++.h>
using namespace std;
int s=1;
int f(int s){
if(s<=100)return f(f(s+11));//递归关系
return s-10;
}
int main(){
while(s!=0){
cin>>s;
if(s!=0){
cout<<"f91("<<s<<")"<<" = "<<f(s)<<endl;
}
}
return 0;
}
T9-T90630 最大公约数与最小公倍数
题目描述
输入两个数,求他们的最大公约数和最小公倍数。(请用递归完成)
输入格式
输入一行,两个正整数。
输出格式
输出一行,两个正整数(这两个正整数的最大公约数和最小公倍数),用一个空格隔开。
输入输出样例
输入
18 20
输出
2 180
说明/提示
所有数据保证小于2^31-1
题目出处
既然是最大公约数,肯定用“辗转相除法”。不了解的同学推荐看这篇C++辗转相除法详解,而最大公倍数就是(设最大公约数为x)(a/x)*(b/x)x=ab/x。可以参考这篇最大公约数和最小公倍数的关系。
这题要递归实现(平常用的是迭代实现)
递归关系:f(a,b)=f(b,a%b)
边界:b==0时return a
附上我一开始的代码:
#include <bits/stdc++.h>
using namespace std;
int f(int a,int b){
int c=b;
b=a%b;
a=c;
if(b==0)return a;
return f(a,b);
}
int main(){
int n,a;
cin>>n>>a;
cout<<f(n,a)<<" "<<n*a/f(a,n);
return 0;
}
发现一种简洁优美的写法:(不会"? : "的同学可以看这篇C++中“?”的意思)
#include <bits/stdc++.h>
using namespace std;
long long a,b;
long long f(long long a,long long b){
return b?f(b,a%b):a;
}
int main(){
cin>>a>>b;
cout<<f(a,b)<<endl;
return 0;
}
T10-T90632 十进制转八进制
题目描述
把任一给定的十进制正整数(<=32000)转换成八进制数后输出。(请用递归完成)
输入格式
一个十进制数。
输出格式
转换后的八进制数
输入输出样例
输入
100
输出
144
题目出处
额……先来演示一下通常做法:
#include <bits/stdc++.h>
using namespace std;
long long a,b,i=1;
int main(){
cin>>a;
while(a>0){
b+=a%8*i;
a/=8;
i*=10;//算位权
}
cout<<b<<endl;
return 0;
}
简洁吧。。。下一步演示递归:
#include <bits/stdc++.h>
using namespace std;
int f(int n){
if(n<8)return n;//也可以将边界改为0
return n%8+f(n/8)*10;
}
int main(){
int n;
cin>>n;
cout<<f(n)<<endl;
return 0;
}
还可以这么做
#include <bits/stdc++.h>
using namespace std;
void f(int n){
if(n/8)f(n/8);
cout<<n%8;//逆序输出
}
int main(){
int n;
cin>>n;
f(n);
return 0;
}
T11-T90633 走台阶
题目描述
楼梯有n阶台阶,上楼可以一步上一阶,也可以一步上二阶。用递归的方法编一程序计算共有多少种不同的走法。(试用递归完成,会出现什么问题,如何解决)
输入格式
一个正整数n(n不超过90)
输出格式
一个整数,表示走法方案数。
输入输出样例
输入
4
输出
5
题目出处
我第一秒想到的是暴力……(造化低了)33分
#include <bits/stdc++.h>
using namespace std;
int n,cnt;
void f(int s){
if(s==n){
++cnt;
return;
}
if(s>n)return;
for(int i=1;i<=2;++i)
f(s+i);
return;
}
int main(){
cin>>n;
f(0);
cout<<cnt<<endl;
return 0;
}
仔细对比数据,竟发现答案是斐波那契数列!!!
自制奇葩递归
#include <bits/stdc++.h>
using namespace std;
long long n,ans,vh[100];
void f(int s){
if(s<3){
if(s==2)cout<<3<<endl;
else cout<<s<<endl;
return;
}
vh[s]=vh[s-1]+vh[s-2];
if(s==n){
cout<<vh[s]<<endl;
return;
}
f(s+1);
return;
}
int main(){
vh[0]=1;//这一行加上只是为了对比发现斐波那契,可删除
vh[1]=1;
vh[2]=2;
cin>>n;
f(3);
return 0;
}
标程:
#include <bits/stdc++.h>
using namespace std;
long long dp[110];//记忆化
long long f(long long n){
if(dp[n])return dp[n];
if(n<=2)return n;//注意这个地方与标准斐波那契不同(因为这一题第n个答案是第(n+1)个斐波那契数,忘记的同学可以向上翻翻T7,这里原是return 1)
else dp[n]=f(n-1)+f(n-2);
return dp[n];
}
int main(){
int n;
cin>>n;
cout<<f(n)<<endl;
return 0;
}
刨根问底的同学可以继续看:
QUESTION:为什么答案是斐波那契数列?
ANSWER:上面我们用的是归纳的方法。下面主要介绍原因:
第1,2个台阶分别有1,2种走法(1)(1+1或2)
而从第3个开始,每一个数目(假设是第n个(n>2))都可以理解为第(n-1)的走法再走1级台阶或第(n-2)的走法再走2级台阶,自然就有第(n-1),第(n-2)的走法总数和数量的方法。