递归 刷题笔记

2019.9.26
阿萧上了C语言课并表示晕递归
我觉得一定要表现一波,加之之前看书也囫囵吞枣,还是做点题加深理解吧,毕竟递归比十连for(草)漂亮多了

汉诺塔问题(只求步数)

公式分析
1阶(1) 1-C
2阶(3) 1-B 2-C 1-C
3阶(7) 1-C 2-B 1-B 3-C 1-A 2-C 1-C
4阶时(15),可通过一定步骤简化为3阶,即4在C上,而123依次排列在B上
步骤如下 1-B 2-C 1-C 3-B 1-A 2-B 1-B 4-C
即先将123整体移动到B柱上
综上 4阶的解决方式为 将123整体移动到B柱 再将123整体移动到C柱
以此类推 5阶时为 将1234 整体移动到B柱 再将1234 整体移动到C柱
……
n阶时的步数:
(n-1)阶的步数*2+1,n>1;
1,n=1;

代码实现:

#include<stdio.h>
int hanoi(int i)
{
	return i==1?1:hanoi(i-1)*2+1;	//函数的返回值又用来调用函数
}

int main()
{
	int i;
	scanf("%d",&i);
	printf("%d",hanoi(i));
	return 0;
}

i 稍微大一点就爆int了,所以这里只用作理解有返回值的函数递归

P2089 烤鸡

题意:
总共十个1-3范围内的加数,要求加出指定的和,并输出不同组合的种数,及组合的具体内容

首先看输出:
如果是先输出具体组合再输出种数的话,只要一个一维数组,记录、输出数组、ans计数、再清空数组继续尝试就行了。
但这里要求是相反的,所以需要把组合的具体内容都存起来,故用到二维数组

然后是具体操作:
很容易想到

 forint i=1,i<=3,i++)

这样的十连for,但是太难看了,递归走起

#include<stdio.h>
int n,kind=0,method[10000][10],methoding[10];
//n为要求的美味程度,method储存方案,methoding储存正在尝试的方案

void tiaoliao(int a,int num){ //a为当前美味程度 num为已加入的调料种数 
	if(num==10){    //若十种调料已全部添加 
		if(a==n){	//若当前方案的美味程度符合要求
			for(int j=0;j<10;j++){
			method[kind][j]=methoding[j];    //将一个一维数组存入一个二维数组的一行中 
			}
			kind++;
		} 
	}
	else if(a>=n) ;//若调料未加完,美味程度就已经达到或超出要求,不执行任何操作 
	else{	//若调料未加完,美味程度也没达到要求 
		for(int i=1;i<=3;i++){
			methoding[num]=i;
			tiaoliao(a+i,num+1); 
		}
	}
}

int main(){
	scanf("%d",&n);
	tiaoliao(0,0);//初始美味程度为0,第一种调料的数组下标为0 
	//输出 
	printf("%d",kind);
	for(int k=0;k<kind;k++){
		printf("\n");//输出完一种方案就换行 
		for(int y=0;y<10;y++)
		printf("%d ",method[k][y]);
	}
	return 0;
} 

详解一下递归过程:
第一次调用丢了(0,0)进去,即初始美味程度为0,从第0种调料(注意数组下标的特殊性)开始。
递归开始,直到十种调料都加入。显然每一次递归都是在尝试下一种调料,所以要写 num+1 。同时要注意当前美味程度的累加,用累加值开始下一次递归,所以是 a+i 。
递归全过程的实质是把十种调料中的每一种的1-3的数值都尝试一遍。当然,n∈[10,30]的时候才会这样,其他都无解。

总结:
1.void类型函数的递归,其实还是用来得出某个或某些值的,如果没有这个值,那就得是执行printf之类的操作。本题中函数递归用来算kind和二维数组method。但函数返回值只能有一个,tiaoliao函数又要两个值,只能是void并且要在参数列表里搞点小操作。
2.要用自定义函数又想避开指针,就得用到全局变量,把主函数与自定义函数都可以修改的东西在最前面声明。注意不要重复声明,之前就是在main函数里又声明了一个n,导致其在tiaoliao函数中无法使用,最后调试了几分钟才发现。
3.代码其实是照着题解边理解边写的……

9.28 新的一天从A题开始

P2799 国王的魔镜

题意:
反复判断是否为回文,若不是则输出字符串长度

注意输出要求。一开始想到要对字符串反复处理修改其真实长度,但完全不需要,只要把数值直接砍一半就好

#include<stdio.h>
#include<string.h>
char necklace[100005];
int length;
void solve(char a[100005])
{
	int flag=1;
	if(length%2==0){   //判断是否偶数 
	for(int i=0;i<length/2;i++){        
	//判断是否回文 (千万注意这个是判断不了奇数字符串的!!) 
		if(necklace[i]!=necklace[length-i-1]) {flag=0;break;}
	}
	if(flag){
	length/=2;//如果是回文 长度减半 继续判断 
	solve(necklace); 
	}
}
}
int main()
{
	scanf("%s",necklace);
	length=strlen(necklace); 
	solve(necklace);
	printf("%d",length);
	return 0;
}
发布了28 篇原创文章 · 获赞 0 · 访问量 687

猜你喜欢

转载自blog.csdn.net/weixin_45561591/article/details/101513046