洛谷【C++编程基础】递归函数初步 专题解题报告

洛谷【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)的走法总数和数量的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GaoGuohao2022

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

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

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

打赏作者

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

抵扣说明:

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

余额充值