# NENUOJ 之 算法2递归A

# 前言

考虑到进入大二上了,所以代码主要以C++的形式给出,倘若读者擅长使用C语言,影响也不会很大~

# A001 猴子吃桃 (opens new window)

# 题目描述

猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第2天又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半另加一个。到第10天早上想再吃时,就只剩下一个桃子了。求第1天共摘了多少个桃子。

# 输入

输入第一行为正整数n,为测试数据组数。后面n行为测试数据,每组测试数据包括两个整数m,k,分别表示第m(m>1)天后剩余的桃子数k(k>=0)。

# 输出

输出猴子第一天摘的桃子数,每组数据占一行。

# 样例输入 复制

2
2  2
3  0

# 样例输出 复制

6
6

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin >> n;
    while(n--){
        int m,k;
        cin >> m >> k;
        for(int i = m ;i > 1;i--){
            k += 1;
            k *= 2;
        }
        cout << k << "\n";
    }
}

# A002 最大公约数 (opens new window)

# 题目描述

输入两个正整数,求其最大公约数。 数论中有一个求最大公约数的算法称为辗转相除法,又称欧几里德算法。其基本思想及执行过程为(设m为两正整数中较大者,n为较小者): (1)令u=m,v=n; (2)取u对v的余数,即r=u%v,如果r的值为0,则此时v的值就是m和n的最大公约数,否则执行第(3)步; (3)u=v,v=r,即u的值为v的值,而v的值为余数r。并转向第(2)步。

# 输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括两个正整数m和n。

# 输出

n行,每行输出对应一个输入。输出应是一个正整数,为m和n的最大公约数。

# 样例输入 复制

2
48 32
15 5

# 样例输出 复制

16
5

# 代码C++ 代码一:

#include<bits/stdc++.h>
using namespace std;
int ggcd(int a,int b){
    return b == 0 ? a : ggcd(b,a % b);
}
int main(){
    int n;
    cin >> n;
    while(n--){
        int a,b;
        cin >> a >> b;
        cout << ggcd(a,b) << "\n";
    }
}

# 代码C++ 代码二:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin >> n;
    while(n--){
        int a,b;
        cin >> a >> b;
        cout << __gcd(a,b) << "\n";
    }
}

# A003 经典的Hanoi(汉诺塔)问题 (opens new window)

# 题目描述

有一个汉诺塔,塔内有A,B,C三个柱子。起初,A柱上有n个盘子,依次由大到小、从下往上堆放,要求将它们全部移到C柱上;在移动过程中可以利用B柱,但每次只能移到一个盘子,且必须使三个柱子上始终保持大盘在下,小盘在上的状态。要求编程输出移动的步骤。

# 输入

输入文件中包含多行,每行为一个整数n,代表初始A柱子上的盘子的个数。

# 输出

对输入文件中的每个整数n列出具体的汉诺塔移动步骤。两组输出之间有一空行。

# 样例输入 复制

3
1

# 样例输出 复制

A-->C
A-->B
C-->B
A-->C
B-->A
B-->C
A-->C

A-->C

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
void move(char a,char b){
    cout << a << "-->" << b << "\n";
}
void hanoi(int n, char a, char b, char c){
	if (n == 1)
		move(a,c);
	else{
		hanoi(n - 1,a,c,b);
		move(a,c);
		hanoi(n - 1,b,a,c);
	} 
}
int main(){
    int n;
    while(cin >> n){
        hanoi(n,'A','B','C');
        cout << "\n";
    }
}

# A004 菲波那契数列 (opens new window)

# 题目描述

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

# 输入

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

# 输出

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

# 样例输入 复制

4
5
2
19
1

# 样例输出 复制

5
1
4181
1

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int f[1000];
void cal(){
    f[0] = 0;
    f[1] = 1;
    for(int i = 2;i <= 20;i++){
        f[i] = f[i - 1] + f[i - 2];
    }
}
int main(){
    int n;
    cin >> n;
    cal();
    while(n--){
        int a;
        cin >> a;
        cout << f[a] << "\n";
    }
}

# A005 另一个Fibonacci数列 (opens new window)

# 题目描述

定义另外一个Fibonacci数列:F(0)=7,F(1)=11,F(n)=F(n-1)+F(n-2),(n≥2)。

# 输入

输入文件中包含多行,每行为一个整数n,n<1000000。

# 输出

对输入文件中的每个整数n,如果F(n)能被3整除,输出yes,否则输出no。

# 样例输入 复制

0
1
2
3

# 样例输出 复制

no
no
yes
no

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    while(cin >> n){
        if(n % 4 == 2) cout << "yes\n";
        else cout << "no\n";
    }
}

# A006 分形(Fractal) (opens new window)

# 题目描述

分形是存在“自相似”的一个物体或一种量,从某种技术角度来说,这种“自相似”是全方位的。 盒形分形定义如下: 度数为1的分形很简单,为: X

度数为2的分形为:

X X

X

X X

如果用B(n-1)代表度数为n-1的盒形分形,则度数为n的盒形分形可以递归地定义为:

B(n-1) B(n-1)

B(n-1)

B(n-1) B(n-1)

你的任务是输出度数为n的盒形分形。

# 输入

输入文件包含多个测试数据,每个测试数据占一行,包含一个正整数n,n ≤ 7。输入文件的最后一行为-1,代表输入结束。

# 输出

对每个测试数据,用符号“X”表示输出盒形分形。在每个测试数据对应的输出之后输出一个短划线符号“-”,在每行的末尾不要输出任何多余的空格,否则得到的是“格式错误”的结果。

# 样例输入 复制

2
3
-1

# 样例输出 复制

X X
 X
X X
-
X X   X X
 X     X
X X   X X
   X X
    X
   X X
X X   X X
 X     X
X X   X X

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3;
char a[MAXN][MAXN];
 
void solve(int x,int y,int n){
    int l = (int)pow(3,n - 2);
    if(n == 1){
        a[x][y] = 'X';
        return;
    }
    if(n){
        solve(x,y,n - 1);
        solve(x + l,y + l,n - 1);
        solve(x + 2*l,y,n - 1);
        solve(x,y + 2*l,n - 1);
        solve(x + 2*l,y + 2*l,n - 1);
    }
     
}
int main(){
     
    int n;
    int cnt = 0;
    while(cin >> n){
        if(n == -1) break;
        for(int i = 0;i < n;i++){
            for(int j = 0;j < n;j++){
                a[i][j] = ' ';
            }
        }
        if(cnt != 0)cout << "-" << "\n";
        cnt++;
        solve(0,0,n);
        int m = (int)pow(3,n - 1);
         for(int i = 0;i < m;i++){
            int maxx = 0;
            for(int j = 0;j < m;j++){
                if(a[i][j] == 'X'){
                    maxx = j;
                }
                else a[i][j] = ' ';
            }
            a[i][maxx + 1] = '\0';
        }
        for(int i = 0;i < m;i ++){
            cout << a[i] << "\n";
        }
    }

}

# A007 二叉树 (opens new window)

# 题目描述

如图所示,由正整数1, 2, 3, ...组成了一棵无限大的二叉树。从某一个结点到根结点(编号是1的结点)都有一条唯一的路径,比如从10到根结点的路径是(10, 5, 2, 1),从4到根结点的路径是(4, 2, 1),从根结点1到根结点的路径上只包含一个结点1,因此路径就是(1)。对于两个结点x和y,假设他们到根结点的路径分别是$(x_1, x_2, ... ,1)$和$(y_1, y_2, ... ,1)$(这里显然有$x = x_1,y = y_1$),那么必然存在两个正整数i和j,使得从xi 和 yj开始,有$x_i = y_j , x_i + 1 = y_j + 1, x_i + 2 = y_j + 2,... $现在的问题就是,给定x和y,要求$x_i$(也就是$y_j$)。

A007.jpg

# 输入

输入有多行,每行包括两个正整数x和y,这两个正整数都不大于1000。

# 输出

每行输出只有一个正整数$x_i$。

# 样例输入 复制

10 4

# 样例输出 复制

2

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int f[1000];
int com(int x,int y){
    if(x == y) return x;
    if(x > y) return com(x / 2,y);
    return com(x,y / 2);
}
int main(){
     
    int x,y;
    while(cin >> x >> y)
        cout << com(x,y) << "\n";
    return 0;
}

# A008 波兰表达式 (opens new window)

# 题目描述

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

# 输入

输入第一行为一个整数n,然后是n行,每行为一组测试数据,其中运算符和运算数之间都用空格分隔,运算数是浮点数。

# 输出

输出n行,每行表达式的值,保留3位小数输出。

# 样例输入 复制

1
* + 11.0 12.0 + 24.0 35.0

# 样例输出 复制

1357.000

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
double solve()
{
    char s[10000];
    cin >> s;
    switch(s[0])
    {
        case '+': return solve() + solve();
        case '-': return solve() - solve();
        case '*': return solve() * solve();
        case '/': return solve() / solve();
        default : return atof(s);
    }
}

int main(){
    
    int n;
    cin >> n;
    while(n--){
        printf("%.3lf\n",solve());
    }
    
    return 0;
}

# A009 放苹果 (opens new window)

# 题目描述

把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

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int dfs(int m,int n){
    if(m==0 || n==1) {
    	return 1;	
	}
	if(n > m){
    	return dfs(m,m);	
	}
	else{
	    return dfs(m,n - 1) + dfs(m - n,n);
	}
}
void solve(){
    int m,n;
    cin >> m >> n;
    cout << dfs(m,n) << "\n";
}
int main(){
    int t = 1;
    cin >> t;
    while(t--) solve();
}

# A010 递归练习1 (opens new window)

# 题目描述

有5个人坐在一起,问第5个人多少岁?他说比第4个人大两岁。问第4个人的岁数,他说比第3个人大两岁。问第3个人的岁数,又说比第2个人大两岁。问第2个人的岁数,说比第1个人大两岁。最后问第1个人的岁数,他说是10岁。请问第5个人多少岁?

# 输入

输入有多行,每行3个整数,依次为m,n,k。m表示一共有几个人,n表示大的岁数,k表示第一个人的岁数。

# 输出

输出第m个人的岁数,每个一行。

# 样例输入 复制

5 2 10

# 样例输出 复制

18

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int m,n,k;
    while(cin >> m >> n >> k){
        cout << k + (m - 1) * n << "\n";
    }
}

# A011 递归练习2 (opens new window)

# 题目描述

根据递推式子C(m,n)=C(m-1,n)+C(m-1,n-1),求组合数C(m,n)。注意递推的终止条件是C(m,1)=m;以及一些m和n取值的一些特殊情况,如m < 0或n < 0或m < n时,C(m,n)值为0,m和n相等时,C(m,n)=1等。

# 输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数m和一个正整数n。

# 输出

输出组合数C(m,n)。

# 样例输入 复制

2
1 100
100 1

# 样例输出 复制

0
100

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int C(int m,int n){
    if(n == 1) return m;
    if(m < 0 || n < 0 || m < n){
        return 0;
    }
    if(m == n) return 1;
    return C(m - 1,n) + C(m - 1,n - 1);
}
int main(){
    int t;
    cin >> t;
    while(t--){
        int m,n;
        cin >> m >> n;
        cout << C(m,n) << "\n";
    }
}

# A012 递归练习3 (opens new window)

# 题目描述

核反应堆中有α和β两种粒子。每秒钟内一个α粒子可以产生3个β粒子,而一个β粒子可以产生1个α粒子和2个β粒子。若在t=0时刻反应堆中有一个α粒子,求t时刻反应堆中分别有多少个α粒子和β粒子。

# 输入

输入有多个整数t,每个一行。

# 输出

输出t时刻反应堆里分别有多少个α粒子和β粒子。

# 样例输入 复制

6

# 样例输出 复制

183 546

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    int a[100],b[100];
    while(cin >> t){
        a[0] = 1;
        b[0] = 0;
        for(int i = 1;i <= t;i++){
            a[i] = b[i - 1];
            b[i] = 3 * a[i - 1] + 2 * b[i - 1];
        }
        cout << a[t] << " " << b[t] << "\n";
    }
}

# A013 红与黑 (opens new window)

# 题目描述

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

# 输入

包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下 1)‘.’:黑色的瓷砖; 2)‘#’:白色的瓷砖; 3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。 当在一行中读入的是两个零时,表示输入结束。

# 输出

对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

# 样例输入 复制

6 9 
....#. 
.....# 
...... 
...... 
...... 
...... 
...... 
#@...# 
.#..#. 
0 0

# 样例输出 复制

45

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
char mx[100][100];
int vis[100][100];
int w,h;
int x,y;
int dfs(int x,int y){
     if(mx[x][y] == '#') return 0;
     if(x <= 0 || y <= 0 || x > h || y > w) return 0;
     if(vis[x][y]) return 0;
     vis[x][y] = 1;
     return 1 + dfs(x - 1,y) + dfs(x + 1,y) + dfs(x,y - 1) + dfs(x,y + 1);
}
int main(){
    
    while(cin >> w >> h){
        if(!w || !h) break;
        for(int i = 1;i <= h;i++){
            for(int j = 1;j <= w;j++){
                cin >> mx[i][j];
                vis[i][j] = 0;
                if(mx[i][j] == '@') x = i,y = j;
            }
        }
        cout << dfs(x,y) << "\n";
    }
    
}

# A014 城堡问题 (opens new window)

# 题目描述

1 2 3 4 5 6 7 #############################

1 # | # | # | | #

#####---#####---#---#####---#

2 # # | # # # # #

#---#####---#####---#####---#

3 # | | # # # # #

#---#########---#####---#---#

4 # # | | | | # #

#############################

(图 1)

‘#’ = Wall ‘|’ = No wall '-' = No wall

图1是一个城堡的地形图。请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分割成m*n(m≤50,n≤50)个方块,每个方块可以有0~4面墙。

# 输入

程序从标准输入设备读入数据。第一行是两个整数,分别是南北向、东西向的方块数。在接下来的输入行里,每个方块用一个数字(0≤p≤50)描述。用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。输入的数据保证城堡至少有两个房间。

# 输出

城堡的房间数、城堡中最大房间所包括的方块数。

# 样例输入 复制

4
7
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13

# 样例输出 复制

5
9

# 代码C++:

#include<bits/stdc++.h>
using namespace std;

const int dir[4][2] = { {0, -1}, {-1, 0}, {0, 1}, {1, 0} };
const int mask[4] = {1, 2, 4, 8};

int rows, cols;
vector<vector<int>> mx, vis;
int ans = 0, maxx = 0, cnt = 0;

void dfs(int x, int y, int step) {
    if (vis[x][y]) return;
    cnt++;
    vis[x][y] = step;

    for (int d = 0; d < 4; ++d) {
        int nx = x + dir[d][0];
        int ny = y + dir[d][1];
        
        if (nx >= 1 && nx <= rows && ny >= 1 && ny <= cols && !vis[nx][ny]) {
            if ((mx[x][y] & mask[d]) == 0) {
                dfs(nx, ny, step);
            }
        }
    }
}

int main() {
    cin >> rows >> cols;
    mx.resize(rows + 1, vector<int>(cols + 1));
    vis.resize(rows + 1, vector<int>(cols + 1, 0));
    
    for (int i = 1; i <= rows; i++) {
        for (int j = 1; j <= cols; j++) {
            cin >> mx[i][j];
        }
    }
    
    for (int i = 1; i <= rows; i++) {
        for (int j = 1; j <= cols; j++) {
            if (!vis[i][j]) {
                ans++;
                cnt = 0;
                dfs(i, j, ans);
                maxx = max(cnt, maxx);
            }
        }
    }
    
    cout << ans << "\n";
    cout << maxx << "\n";
    
    return 0;
}

# A015 分解因式 (opens new window)

# 题目描述

给出一个正整数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

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int cnt = 1;
void dfs(int m,int a){
    for(int i = m;i * i <= a;i++){
        if(a % i == 0){
            cnt++;
            dfs(i,a / i);
        }
    }
}
int main(){
    int n;
    cin >> n;
    while(n--){
        cnt = 1;
        int a;
        cin >> a;
        dfs(2,a);
        cout << cnt << "\n";
    }
}

# A016 数字拼凑 (opens new window)

# 题目描述

现在给你这样一个任务,要求找出具有下列性质数的个数(包含输入的正整数 n)。 先输入一个正整数 n(n <= 500),然后对此正整数按照如下方法进行处理:

  1. 不作任何处理;
  2. 在它的左边拼接一个正整数,但该正整数不能超过原数的一半或者是上一个被拼接数的一半;
  3. 加上数后,继续按此规则进行处理,直到不能再加正整数为止。

# 输入

一个正整数n。

# 输出

一个正整数,表示具有该性质数的个数。

# 样例输入 复制

6

# 样例输出 复制

6

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int cnt;
void dfs(int m){
    cnt++;
    for (int i = 1;i <= m/2;i++) 
      dfs(i);
}
int main(){
    int n;
    cin >> n;
    dfs(n);
    cout << cnt << "\n";
    return 0;
}