C 数据结构之递归:汉诺塔3、Prime ring problem和Oil Deposit

所谓递归即函数调用函数本身,调用方式按照问题不同而人为定义,这种调用方式被称为递归。同时,为了不使这样的递归无限发生,我们必须设定递归的出口,即函数到达某种条件时停止递归。在此以三例分别介绍递归的魅力,以及在回溯枚举和图的遍历上的应用。

1.汉诺塔3 不允许小盘在大盘上,不允许直接从最右移向最左,或最左到最右,现有N盘,至少移动多少次

输入 每次输入一个N 大于1小于35
1 3 12

输出 2 6 531440  

思路:k个盘,将最底下的圆盘移到第三根柱上,首先将k-1个盘移动到第三盘,最大圆盘到第二柱。再次将k-1盘
移动到第一盘,大盘移到第三柱,最后将k-1盘移动到第三盘。若将k盘从第一柱移动到第三需要F[k]次,那么
F[k]等于移动3次k-1个盘加2,k-1 盘1柱到3柱, 大盘1到2,k-1 3 到1,大盘2到3,k-1盘1到3
即F[k]=F[k-1]*3+2.当x=1是,移动2次

#include<stdio.h>
#include<string.h>
long long F(int num)
{
	if(num==1)
	   return 2;
	else
	   return 3*F(num-1)+2;
} 
int main()
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		printf("%lld\n",F(n));
	}
	return 0;
}

递归函数本例没有关注具体的移动的方法,而是将当前问题与规模更小的问题联系起来 ,并利用此关系确定递归关系式。

2.给定1到n数字,将数字依次填入环中,使环中任意两个相邻的数字的和为素数。对于给定的n,按字典顺序由小到大输出所有符合条件的解。

 思路:采用回溯枚举每一个值,当第一个数为1确定时,尝试放第二个数,使其和1的和为素数,然后在尝试放第三个数,直到所有数全部放入环中,且最后一个数也是素数,则答案正确输出。若在尝试放数的过程中,发现无论放置任何之前未被使用的数均不能满足条件,那么回溯改变上一个数,直至产生所要的答案或不再存在更多的解。

#include<stdio.h>
#include<string.h>
int ans[22];//保存环中每一个被放入的数 
bool hash[22];//标记之前已经被放入环中的数 
int n;
int prim[]={2,3,5,7,11,13,17,19,23,29,31,37,41};
bool jude(int x)//判断一个数是否为素数 
{
	for(int i=0;i<13;i++)
	{
		if(x==prim[i]) return true;
	}
	return false;
} 
void check() //检查输出由回溯枚举得到的解 
{
	if(jude(ans[n]+ans[1])==false) return;//判断首尾 
	for(int i=1;i<=n;i++)
	{
		if(i!=1) printf(" ");//输出解 
		printf("%d",ans[i]);
	}
	printf("\n");
}
void DFS(int num) //递归枚举,num为当前已放入环中的数字 
{
	if(num>1) //当放入数字大于一个时,判断最后两个数字和是否为素数,不是返回继续枚举第num个数 
	  if(jude(ans[num]+ans[num-1])==false) return;
	  if(num==n)
	  {//若为n,检查输出 
	  	check();
	  	return;//返回继续枚举下一组解 
	  }
	  for(int i=2;i<=n;i++)//放入下一个数 
	  {
	  	if(hash[i]==false)
	  	{//若i没被放入环中 
	  		hash[i]=true;//标记i已使用 
	  		ans[num+1]=i;//将这个数字放入ans数组 
	  		DFS(num+1);//继续尝试放入下一个数 
	  		hash[i]=false;//当回溯惠枚举该位数字时,将i重新标记为未使用 
	  	}
	  }
}
int main()
{
	int cas=0;//记录cas数 
	while(scanf("%d",&n)!=EOF)
	{
		cas++;
		for(int i=0;i<22;i++)
		  hash[i]=false;
		  ans[1]=1;
		  printf("case %d:\n",cas);
		  hash[1]=true;
		  DFS(1);
		  printf("\n");
	}
	return 0;
}
3.在给定的n*m图中,确定有几个@块。任意对@块均直接或间接互相联通,两个@直接相邻
或对角相邻均视为联通。输入
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@*****@
5 5
****@
*@@*@
*@**@
*@@*@
@@**@

输出 0 1 2 2 

思路:首先对图上所有位置设置一个标记位,表示该位是否被计算过,且该标记只对图上的@有效。这样从左至右,从上往下的顺序依次遍历地图上所有位置,若遍历到@,且该点未被标记,则所有与其直接相邻,或间接相邻的@点与其一起组成一个块,该快即为一个我们需要计算的块。这样当所有的位置被遍历后,即得到了所需答案。

#include<stdio.h>
char maze[101][101];
bool mark[101][101];
int n,m;
int go[][2]={1,0,-1,0,0,1,0,-1,1,1,1,-1,-1,-1,-1,1};//8个相邻点与当前点坐标差 
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>n||ny<1||ny>m) continue;//坐标在地图外 
		if(maze[nx][ny]=='*') continue;//若该位置不是@继续 
		if(mark[nx][ny]==true) continue;//该位置已经计算过,继续 
		mark[nx][ny]=true;//标记该位置为已计算 
		DFS(nx,ny);//递归查询与该位置相邻的所有点 
	}
	return;
} 
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(n==0&&m==0) break;
		for(int i=1;i<=n;i++)
		{
			scanf("%s",maze[i]+1);
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				mark[i][j]=false;
			}
		}
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)//按顺序遍历图中所有位置 
			{
				if(mark[i][j]==true) continue;
				if(maze[i][j]=='*')  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、付费专栏及课程。

余额充值