NOI题库答案 2.2基本算法之递归和自调用函数

1696:逆波兰表达式

总时间限制:

1000ms

内存限制:

65536kB

描述

逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的逆波兰表示法为* + 2 3 4。本题求解逆波兰表达式的值,其中运算符包括+ - * /四个。

输入

输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数。

输出

输出为一行,表达式的值。
可直接用printf("%f\n", v)输出表达式的值v。

样例输入

* + 11.0 12.0 + 24.0 35.0

样例输出

1357.000000

因为是逆波兰表达式,所以我们是从后往前算的,遇到一个计算符号,就计算这个计算符后面两个数字的值,

如何转化为递归呢?

#include
#include
using namespace std;

double exp(){
	char s[20];
	cin >> s;
	switch(s[0]){
		case '+' :return exp() + exp();
		case '-' :return exp() - exp();
		case '*' :return exp() * exp();
		case '/' :return exp() / exp();
		default : return atof(s);	//2atof(将字串转换成浮点型数) 
		break;
	}
}
int main(){
	printf("%f", exp());
	return 0;
} 

1750:全排列

总时间限制:

1000ms

内存限制:

65536kB

描述

给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。 我们假设对于小写字母有'a' < 'b' < ... < 'y' < 'z',而且给定的字符串中的字母已经按照从小到大的顺序排列。

输入

输入只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。

输出

输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定义:

已知S = s1s2...sk , T = t1t2...tk,则S < T 等价于,存在p (1 <= p <= k),使得
s1 = t1, s2 = t2, ..., sp - 1 = tp - 1, sp < tp成立。

样例输入

abc

样例输出

abc 
acb 
bac 
bca 
cab 
cba

全排列递归

我们外+一个字符串数组了来保存我们排列的结果,

我们从0开始排列,先一S[0] 作为第一个,然后从 后面选,每选一个就标记一下,避免重选,然后计算现在排列了几个,当我们回来时要把标记去掉,因为这种情况已经排完了。不影响下次排列。

退出边界是全部字符都被排列到  t[  ]  中,输出即可。

#include
#include
#include

using namespace std;
string s;
int len;
bool vis[100];
string t;
void quanpai(int index){
	if(index == len){
		for(int i = 0; i < len; i++){
			cout << t[i]; 
		}
		cout << endl;
		return ;
	}
	for(int i = 0; i < len; i++){
		if(vis[i] == 0)
		{
			t[index] = s[i];
			vis[i] = 1;
			quanpai(index + 1);
			vis[i] = 0;
		}
	}
}
int main(){

	cin >> s;
	len = s.length();
	memset(vis, 0, sizeof(vis));
	quanpai(0);
	return 0;
} 

1751:分解因数

总时间限制:

1000ms

内存限制:

65536kB

描述

给出一个正整数a,要求分解成若干个正整数的乘积,即a = a1 * a2 * a3 * ... * an,并且1 < a1 <= a2 <= a3 <= ... <= an,问这样的分解的种数有多少。注意到a = a也是一种分解。

输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a (1 < a < 32768)

输出

n行,每行输出对应一个输入。输出应是一个正整数,指明满足要求的分解的种数

样例输入

2 
2 
20

样例输出

1 
4

思路:

给你一个数让你求出所有因数相乘的情况

比如20 

1)  20 * 1

2)2 * 2 * 5

3)2 * 10

4)4 * 5

共  4 种

我们可以从2 开始,然后每次去看 2 * x = n有多少种情况 然后去看  3 一直到 n,

这样我们就可以写出递归程序了,退出边界就是n =  1。

#include
#include
#include

using namespace std;

int DFS(int n, int m){
	int count = 0;
	if(n == 1)
		return 1;
	for(int i = m; i <= n; i++){
		if(n % i == 0)
			count += DFS(n/i, i); 
	}
	return count;
}

int main(){
	int t; 
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		cout << DFS(n,2) << endl;
	}	
	return 0;
} 

1755:菲波那契数列

总时间限制:

1000ms

内存限制:

65536kB

描述

菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。
给出一个正整数a,要求菲波那契数列中第a个数是多少。

输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a(1 <= a <= 20)

输出

输出有n行,每行输出对应一个输入。输出应是一个正整数,为菲波那契数列中第a个数的大小样例输入

4
5
2
19
1

样例输出

5
1
4181
1

解题思路:

你要算 f(n) 的值,你就需要算 f ( n -2 )  和f(n-1) 的值,这样递归的条件就出来了 然后你再去推下边界。

#include
#include
#include
using namespace std;
int DFS(int n){
	if(n == 1)
	return 1;
	if(n == 2)
	return 1;
	else
	return DFS(n-1) + DFS(n-2);	
}

int main(){
	int t; 
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		cout << DFS(n) << endl;
	}	
	return 0;
} 

1788:Pell数列

总时间限制:

3000ms

内存限制:

65536kB

描述

Pell数列a1, a2, a3, ...的定义是这样的,a1 = 1, a2 = 2, ... , an = 2 * an − 1 + an - 2 (n > 2)。
给出一个正整数k,要求Pell数列的第k项模上32767是多少。

输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数k (1 ≤ k < 1000000)。

输出

n行,每行输出对应一个输入。输出应是一个非负整数。样例输入

2
1
8

样例输出

1 
408

解题思路:给上题一样,有递归方程和条件.但是单纯的搜索会超时,你需要一个数组来保存算过的值,更可以减少重复搜索。

#include
#include
#include

using namespace std;
long long a[1000005];
long long DFS(int n){
	if(a[n] != 0)
	return a[n];
	else
	return a[n] = (DFS(n-1) * 2 + DFS(n-2)) % 32767;	
}

int main(){
	int t; 
	cin >> t;
	a[1] = 1;
	a[2] = 2;
	a[0] = 1;
	while(t--){
		long long n;
		cin >> n;
		cout << DFS(n) << endl;
	}	
	return 0;
} 

3089:爬楼梯

总时间限制:

1000ms

内存限制:

65536kB

描述

树老师爬楼梯,他可以每次走1级或者2级,输入楼梯的级数,求不同的走法数
例如:楼梯一共有3级,他可以每次都走一级,或者第一次走一级,第二次走两级
也可以第一次走两级,第二次走一级,一共3种方法。

输入

输入包含若干行,每行包含一个正整数N,代表楼梯级数,1 <= N <= 30

输出

不同的走法数,每一行输入对应一行输出

样例输入

5 
8 
10

样例输出

8 
34 
89

解题思路:

我们 我们想一下现在我们在第n 个台阶,然后向下走有2种方式,要么向下1 步,要么向下2步

这就可以递归了

#include
#include
#include

using namespace std;
long long a[1000005];
long long DFS(int n){
	if(a[n] != 0)
	return a[n];
	else
	return a[n] = DFS(n-1) + DFS(n-2);	
}
int main(){
	
	a[1] = 1;
	a[2] = 2;
	long long n;
	while(cin >> n){
		
		cout << DFS(n) << endl;
	}	
	return 0;
} 

6261:汉诺塔问题

总时间限制:

1000ms

内存限制:

65536kB

描述

约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到中间的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
这是一个著名的问题,几乎所有的教材上都有这个问题。由于条件是一次只能移动一个盘,且不允许大盘放在小盘上面,所以64个盘的移动次数是:18,446,744,073,709,551,615
这是一个天文数字,若每一微秒可能计算(并不输出)一次移动,那么也需要几乎一百万年。我们仅能找出问题的解决方法并解决较小N值时的汉诺塔,但很难用计算机解决64层的汉诺塔。

假定圆盘从小到大编号为1, 2, ...

输入

输入为一个整数后面跟三个单字符字符串。
整数为盘子的数目,后三个字符表示三个杆子的编号。

输出

输出每一步移动盘子的记录。一次移动一行。
每次移动的记录为例如 a->3->b 的形式,即把编号为3的盘子从a杆移至b杆。

样例输入

2 a b c

样例输出

a->1->c 
a->2->b 
c->1->b

解题思路:

这个题是把所有的盘子移动到中间的盘子中

#include
#include
#include
using namespace std;

void DFS(int n, char a, char b, char c){
	if(n == 1) 
		cout << a << "->" << n << "->" << b << endl;     //只有一个盘子把他从A移动到 B
	else{
		DFS(n - 1, a, c, b);    //把n-1个盘子,从A,经过B移动到C
		cout << a << "->" << n << "->" << b << endl;    //把最后一个盘子从A 移动到 B
		DFS(n - 1, c, b, a);    //再把 n - 1个盘子从C经过A移动到B
	}
	
}

int main(){
	int t;
	cin >> t;
	char a, b, c;
	cin >> a >> b >> c;
	DFS(t, a, b, c);
	return 0;
} 

666:放苹果

总时间限制:

1000ms

内存限制:

65536kB

描述

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

输入

第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。

输出

对输入的每组数据M和N,用一行输出相应的K。

样例输入

1 
7 3

样例输出

8

、解题思路:

你有n个苹果m个盘子,然后你进行放苹果的游戏,你放的话是有2种情况的是把,

1)有盘子空着

2)没有盘子空着

有盘子空着时 就是 n 个 苹果放到m - 1 个盘子中去

没有盘子空着时就是 n - m 个苹果再放到m 个盘子中去。

然后呢 当 n < m 时 是不是 盘子比苹果多,因为盘子是一样的,所以我们这个问题就转化成了 n 个苹果放到n 个盘子里了。

 

#include
#include
#include
using namespace std;

long long DFS(int n, int m){
	if(m == 0)        //没有盘子
		return 0;    //没有方法
	if(n == 0)        //没有苹果
		return 1;    //有一个方法
	if(m > n)
		return DFS(n, n);
	return DFS(n, m - 1) + DFS(n - m, m); 
}

int main(){
	int t;
	cin >> t;
	long long n, m;
	while(t--){
		cin >> n >> m;
		cout << DFS(n, m) << endl;
	}	
	return 0;
} 

7592:求最大公约数问题

总时间限制:

1000ms

内存限制:

65536kB

描述

给定两个正整数,求它们的最大公约数。

输入

输入一行,包含两个正整数(<1,000,000,000)。

输出

输出一个正整数,即这两个正整数的最大公约数。

样例输入

6 9

样例输出

3

提示

求最大公约数可以使用辗转相除法:
假设a > b > 0,那么a和b的最大公约数等于b和a%b的最大公约数,然后把b和a%b作为新一轮的输入。
由于这个过程会一直递减,直到a%b等于0的时候,b的值就是所要求的最大公约数。
比如:
9和6的最大公约数等于6和9%6=3的最大公约数。
由于6%3==0,所以最大公约数为3。

这个题就是辗转相除法

#include
using namespace std;
int gcd(int n, int m){
	if(m == 0)
	return n;
	else
	return gcd (m, n % m);
}
int main (){
	int n, m;
	cin >> n >> m;
	cout << gcd(n, m);
	return 0;
}

 

你可能感兴趣的:(NOI)