【北大计算概论A】【期末复习】递归

学习递归可能是初学者接触编程后较难理解的地方,递归函数在本身“没有写完”时就自己调用了自己。可以尝试通过代码模拟加深对于递归状态设计和递归出口的认识,本文依旧从课上题目和课下练习入手帮助大家进行此部分的复习。

题目一:流感感染

有一批易感人群住在网格状的宿舍区内,宿舍区为n*n的矩阵,每个格点为一个房间,房间里可能住人,也可能空着。在第一天,有些房间里的人得了流感,以后每天,得流感的人会使其邻居传染上流感,(已经得病的不变),空房间不会传染。请输出第m天得流感的人数。

关于输入:第一行一个数字n,n不超过100,表示有n*n的宿舍房间。
接下来的n行,每行n个字符,’.’表示第一天该房间住着健康的人,’#’表示该房间空着,’@’表示第一天该房间住着得流感的人。
接下来的一行是一个整数m,m不超过100.

代码实现:以天数作为阶段划分方式(对于简单的题目,时间和空间是常用的阶段划分依据,其实就是依据题目的要求设计状态)。

#include <iostream>
#include <cstring>
using namespace std;
char map[105][105];
bool visit[105][105];
int n, k;
int gan = 0; // 记录感染的人数,每天新增的感染人数总和即为答案
void liugan(int day) {
	memset(visit, true, sizeof(visit));
	if (day == k) return; // 递归出口,计算到第k天即可
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (map[i][j] == '@' && visit[i][j] == true) {
				visit[i][j] = false;
				if (i >= 1) { 
					if (map[i - 1][j] == '.') {
						gan++;
						map[i - 1][j] = '@';
					}
				} if (i <= n - 2) {
					if (map[i + 1][j] == '.') {
						gan++;
						map[i + 1][j] = '@';
						visit[i + 1][j] = false;
					}
				} if (j >= 1) {
					if (map[i][j - 1] == '.') {
						gan++; 
						map[i][j - 1] = '@';
					}
				} if (j <= n - 2) {
					if (map[i][j + 1] == '.') {
						gan++;
						map[i][j + 1] = '@';
						visit[i][j + 1] = false;
					}
				}
			}
		}
	}
	day++;
	liugan(day); // 递归调用下一天
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> map[i][j];
			if (map[i][j] == '@') gan++; // 最初就感染的人数
		}
	}
	cin >> k;	
	liugan(1);
	cout << gan << endl;
	return 0;
}
题目二:棋盘问题

在一个给定大小的棋盘上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

关于输入:输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

关于输出:对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

【代码实现】做过八皇后的同学都知道这是一个变体,我们状态的划分是第几列,此外还要加上放了几个棋子。

#include <iostream>
using namespace std;
int n, k;
char map[10][10];
int cnt;
bool col[10]; // 记录这一行是否已经有棋子了 
void chess(int x, int num) { // x表示当前的列,放第num个棋子 
	if (x == n) {
		if (num == k + 1) cnt++;
		return;
	}
	if (num == k + 1) {
		cnt++;
		return;
	}
    // 当前列不放棋子
	chess(x + 1, num);
    // 当前列放棋子
	for (int i = 0; i < n; i++) { // 枚举行数 
		if (col[i] || map[i][x] == '.') continue;
		col[i] = true;
		chess(x + 1, num + 1);
		col[i] = false; // 回溯
	}
}
int main() {
	while (1) {
		cnt = 0;
		cin >> n >> k;
		if (n == -1 && k == -1) break;
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				cin >> map[i][j];
		chess(0, 1);
		cout << cnt << endl;
	}
	return 0;
}
题目三:集合里的乘法

给定一个目标数T和一个整数集合S,判断是否存在S的一个非空子集,子集中的数相乘的积为T。

代码实现:本题和上一题极为相似,可以对应来看。

#include <iostream>
using namespace std;
int t, n;
int s[20];
bool flag;
void f(int x, int sum) { // 枚举第x个元素,sum为当前的乘积 
	if (x == n + 1) {
		if (sum == t) flag = 1;
		return;
	}
	if (sum == t) {
		flag = 1;
		return;
	}
    // 不选当前元素
	f(x + 1, sum);
    // 选择当前元素
	f(x + 1, sum * s[x]);
}
int main() {
	cin >> t >> n;
	for (int i = 1; i <= n; i++) cin >> s[i];
	f(1, 1);
	if (flag) cout << "YES" << endl;
	else cout << "NO" << endl;
	return 0;
} 
题目四:左手定则

玩家在玩迷宫游戏,经过多次Trial and Error决定用左手定则对迷宫进行探索。左手定则:在每一个路口,我们都选择最左边的方向,左转的优先级最高,其次为向前,最后为右转,如果实在走进了一个死胡同,那就连续右转两次,回头向后走。稍微研究一下这种走迷宫的方法,我们就发现在这个过程中,事实上我们的左手可以始终放在墙上。
但是呢,左手定则并不能保证遍历到迷宫中的每一个点。悲剧的是,某项重要的通关道具被放在了这个迷宫中……幸运的是,游戏迷宫的地图可以绘制出来,现在请求你的帮助。对于一个给定的地图,他想知道是不是能够通过左手定则取得这件道具。

关于输入:多组数据。
对于每组数据,第一行有两个整数N,M代表接下来有n行字符串,每行m个字符,其中0 < n <= 100, 0 < m <= 100,表示一个n × m的迷宫。
其中‘#’表示墙,‘S’表示起点,‘T’表示道具,‘.’表示空地。
接下来是一个方向(NSWE),表示起始面向的方向。
数据保证最外一圈都是墙。

关于输出:对于每组数据输出一行。‘YES’表示可以到达,‘NO’表示无法到达。

代码实现:因为每次转弯玩家的朝向都会改变,因此增加了opt这个状态表示朝向。如果之前到过当前的点而且和之前来的方向相同,就标记为已经走过。

#include <iostream>
#include <cstring>
using namespace std;
int n, m, sx, sy, ex, ey;
char map[110][110], dir;
bool flag;
bool visited[110][110][5];
bool inmap(int x, int y) {
	return (x >= 1) && (x <= n) && (y >= 1) && (y <= m) && map[x][y] != '#';
}
void dfs(int x, int y, int opt) { // opt记录上一次的运动方向 
	visited[x][y][opt] = 1;
	if (x == ex && y == ey) {
		flag = 1;
		return;
	}
	if (flag) return;
	if (x != sx && y != sy && map[x][y] == 'S') return;
	if (opt == 1) { // 1表示上一次是向北 
		if (inmap(x, y - 1)) {
			if (visited[x][y - 1][2]) return;
			dfs(x, y - 1, 2); // 2表示向西 
		}
		else if (inmap(x - 1, y)) {
			if (visited[x - 1][y][1]) return;
			dfs(x - 1, y, 1); // 1继续向北 
		}
		else if (inmap(x, y + 1)) {
			if (visited[x][y + 1][3]) return;
			dfs(x, y + 1, 3); // 3表示向东 
		}
		else { // 走到死胡同 
			if (visited[x + 1][y][4]) return;
			dfs(x + 1, y, 4); // 4表示向南 
		}
	}
	else if (opt == 2) { // 向西 
		if (inmap(x + 1, y)) {
			if (visited[x + 1][y][4]) return;
			dfs(x + 1, y, 4); // 向南 
		}
		else if (inmap(x, y - 1)) {
			if (visited[x][y - 1][2]) return;
			dfs(x, y - 1, 2); // 向西
		}
		else if (inmap(x - 1, y)) {
			if (visited[x - 1][y][1]) return;
			dfs(x - 1, y, 1);
		}
		else {
			if (visited[x][y + 1][3]) return;
			dfs(x, y + 1, 3);
		}
	}
	else if (opt == 3) { // 向东 
		if (inmap(x - 1, y)) {
			if (visited[x - 1][y][1]) return;
			dfs(x - 1, y, 1); // 向北 
		}
		else if (inmap(x, y + 1)) {
			if (visited[x][y + 1][3]) return;
			dfs(x, y + 1, 3); // 向东 
		}
		else if (inmap(x + 1, y)) {
			if (visited[x + 1][y][4]) return;
			dfs(x + 1, y, 4); // 向南 
		}
		else {
			if (visited[x][y - 1][2]) return;
			dfs(x, y - 1, 2); // 向西 
		}
	}
	else if (opt == 4) { // 向南 
		if (inmap(x, y + 1)) {
			if (visited[x][y + 1][3]) return;
			dfs(x, y + 1, 3); // 向东
		}
		else if (inmap(x + 1, y)) {
			if (visited[x + 1][y][4]) return;
			dfs(x + 1, y, 4); // 向南
		}
		else if (inmap(x, y - 1)) {
			if (visited[x][y - 1][2]) return;
			dfs(x, y - 1, 2); // 向西
		}
		else {
			if (visited[x - 1][y][1]) return;
			dfs(x - 1, y, 1); // 向北 
		}
	}
}
int main() {
	while (cin >> n >> m) {
		memset(visited, 0, sizeof(visited));
		flag = 0;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cin >> map[i][j];
				if (map[i][j] == 'S') sx = i, sy = j;
				if (map[i][j] == 'T') ex = i, ey = j;
			}
		}
		cin >> dir;
		if (dir == 'N') dfs(sx, sy, 1);
		else if (dir == 'E') dfs(sx, sy, 3);
		else if (dir == 'S') dfs(sx, sy, 4);
		else dfs(sx, sy, 2);
		if (flag) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值