所谓递归即函数调用函数本身,调用方式按照问题不同而人为定义,这种调用方式被称为递归。同时,为了不使这样的递归无限发生,我们必须设定递归的出口,即函数到达某种条件时停止递归。在此以三例分别介绍递归的魅力,以及在回溯枚举和图的遍历上的应用。
1.汉诺塔3 不允许小盘在大盘上,不允许直接从最右移向最左,或最左到最右,现有N盘,至少移动多少次
输入 每次输入一个N 大于1小于351 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;
}