递归

好久没更了,但是还是坚持了每天至少一题。。
今天更递归,虽然我老是在递归上犯迷糊,但是背也得背下来

先看递归经典问题
汉诺塔问题

汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

  • 用递归解决问题,一个关键点是要有递归结束的条件
  • 当只有一个盘子的时候,直接就是A->C,这也是递归结束的条件
  • 当有两个盘子的时候,我们知道需要这样移动,A->B, A->C, B->C。
  • 当有三个或三个以上的盘子的时候,我们这样来考虑把最下面的盘子当做一块,其他盘子当做一块,那么,就简化成了上一步。A柱子是源,B柱子是当做临时转换用的源,C是目的
  • 需要注意的是,当经过上一步后,除了最下面的盘子其他的已经到了B柱子上了,这个时候,A柱子就是当做临时转换用的了,B柱子是源,C是目的。所以当k不等于1的时候,会回调两次函数,第一次参数的顺序是k,a,c,b。第二次是k,b,a,c

   

void  move(char a, char b) {
	printf("%c->%c\n", a, b);
}
void hanuota(int k, char a, char b, char c) {
	if (k == 1) move(a,c);
	else {
		hanuota(k - 1, a, c, b);
		move(a, c);
		hanuota(k - 1, b, a, c);
	}
}
int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		hanuota(n, 'A', 'B', 'C');
	}
	return 0;
}

变形汉诺塔问题 九度1458
题目描述:

约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。现在我们改变游戏的玩法,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到下盘的上面。Daisy已经做过原来的汉诺塔问题和汉诺塔II,但碰到这个问题时,她想了很久都不能解决,现在请你帮助她。现在有N个圆盘,她至少多少次移动才能把这些圆盘从最左边移到最右边?

输入:

包含多组数据,每次输入一个N值(1<=N=35)。

输出:

对于每组数据,输出移动最小的次数

#include<stdio.h>
long long F(int x) {
	if (x == 1) return 2;
	else {
		return F(x - 1) * 3 + 2; //每次需要移动的总次数
	}
}
int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		printf("%d\n", F(n));
	}
}

应用:

例1 全排列问题

九度1459 素数环问题
题目描述:

A ring is compose of n circles as shown in diagram. Put natural number 1, 2, ..., n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.
Note: the number of first circle should always be 1.


输入:

n (1 < n < 17).

输出:

The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.
You are to write a program that completes above process.
Print a blank line after each case.

//素数环问题
#include<stdio.h>
using namespace std;
bool hash[22];
int ans[22];//保存当前符合要求的数
int n; //输入的数字个数
int prime[] = { 2,3,5,7,11,13,17,19,23,29,31,37,41 };
bool judge(int x){
	for (int i = 0; i < 13; i++) {
		if (x == prime[i]) return true;
	}
	return false;
}
void check() {
	if (judge(ans[1]+ans[n]) == false) return;
	for (int i = 1; i <= n; i++) {
		if (i != 1) printf(" ");
		printf("%d", ans[i]);
	}
	printf("\n");
}
void DFS(int num){
	if (num > 1) {
		if (judge(ans[num] + ans[num - 1]) == false) return;
	}
	if (num == n) {
		check();
		return;
	}
	for (int i = 2; i <= n; i++) {
		if (hash[i] == false) {
			hash[i] = true;
			ans[num + 1] = i;
			DFS(num + 1);
			hash[i] = false; //如果不满足,则又回溯回来
		}
	}
}
int main() {
	int cas = 1;
	while(scanf("%d", &n) != EOF) {
		for (int i = 0; i < 22; i++) {
			ans[i] = 0;
			hash[i] = false;
		}
		ans[1] = 1;
		hash[1] = true;
		printf("Case: %d\n", cas);
		DFS(1);
		cas++;
	}
	return 0;
}

九度1120 列出全排列

题目描述:
给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。
我们假设对于小写字母有'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

提示:
每组样例输出结束后要再输出一个回车。


#include<stdio.h>
#include<string.h>
using namespace std;
int len=0;
bool hash[7];
char a[7],s[7];
void dfs(int m) {
	int i;
	if (m == len) puts(a);
	else {
		for (i = 0; i < len; i++) {
			if (hash[i] == false) {
				hash[i] = true;
				a[m] = s[i];
				dfs(m + 1);
				hash[i] = false; //哈希回溯
			}
		}
	}
}
int main() {
	while (scanf("%s", s) != EOF) {
		int i, j;
		len = strlen(s);
		for (i = 0; i < len; i++) {
			hash[i] = false;
		}
		a[len] = '\0';
		dfs(0);
	}
}

例2  图的遍历(类似DFS,但是简练很多)

九度1460

题目描述:

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid.

输入:

The input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either `*', representing the absence of oil, or `@', representing an oil pocket.

输出:

For each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets

#include<stdio.h>
using namespace std;
bool mark[101][101];
char maze[101][101];
int m, n;
int go[][2] = { 1,0,
			   -1,0,
			   0,1,
			   0,-1,
			   1,1,
			   1,-1,
			   -1,-1,
			   -1,1 };
void DFS(int x,int y){
	for (int i = 0; i < 8; i++) {
			int nx = x + go[i][0];
			int ny = y + go[i][1];
			if (nx<1 || nx>m || ny<1 || ny>n) continue;
			if (mark[nx][ny] == true) continue;
			if (maze[nx][ny] == '*') continue;
			mark[nx][ny] = true;
			DFS(nx, ny);
	}
	return;
}
int main() {
	while (scanf("%d%d", &m, &n) != EOF) {
		int i, j;
		for (i = 1; i <= m; i++) {
			getchar();
			for (j = 1; j <= n; j++) {
				scanf("%c", &maze[i][j]);
				mark[i][j] = false;
			}
		}
		int ans = 0;
		for (i = 1; i <= m; i++) {
			for (j = 1; j <= n; j++) {
				if (maze[i][j] == '*') continue;
				if (mark[i][j] == true) continue;
				DFS(i, j);
				ans++;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值