递归算法:有点像高中学的数列知识,定义说不清楚,可以百度,最常见的斐波那契数列的通项公式就可以使用递归的方法实现,还有一个就是汉诺塔的问题,又称河内塔,递归实现的方法是:
#include<iostream>
#include<cstdio>
using namespace std;
int step,n;
void hanoi(int m,char a,char b,char c)//将a上编号为1到m的圆盘从a经过b移动到c
{ if(m==1) printf("%d:Move %d from %c to %c\n",step++,m,a,c);//打印移动过程,将编号为m的圆盘由a移动到c
if(m==1) return ;
hanoi(m-1,a,c,b);//将a上编号为1到m-1的圆盘从a经过c移动到b
printf("%d:Move %d from %c to %c\n",step++,m,a,c);//将编号为m的圆盘从a移到c
hanoi(m-1,b,a,c);//将b上编号为1到m-1的圆盘从b经过a移动到c
}
int main()
{ while(cin>>n,n!=-1)
{ step=1;
hanoi(n,'a','b','c');
printf("%d\n",step-1);//n个圆盘移动到目标过程的步数为:2^n-1
}
return 0;
}
以汉诺塔为内容的题目HDU上有很多,从汉诺塔(I).....一直到了好多,可以做下~~~
例题2:poj 2083题目链接:http://poj.org/problem?id=2083
题目分析:题目已经给出里一个模型,这五个位置的图形都是一样的,只是位置不同,可以先计算出对于一般的n这个图形输出有多少行,可知有这样一个递推的关系式:mi[n]=3*mi[n-1],即mi[n-1]=3^(n-1),所以当n=7时为729行,我们可以设左上角的图形的位置为(Startx,Starty),其余的坐标就都可以用这个表示出来了,
B(n - 1) (左上角) B(n - 1)(右上角)
(Startx,Starty) (Startx,Starty+2*mi[m-2])
B(n - 1)(中间)
(Startx+mi[m-1],Starty+mi[m-2])
B(n - 1)(左下角) B(n - 1)(右下角)
(Startx+2*mi[m-1],Starty) (Startx+2*mi[m-1],Starty+2*mi[m-1])
#include<iostream>
#include<cstring>
using namespace std;
const int MAX=729;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int n,mi[10]={1,3,9,27,81,243,729,2187};
char map[MAX][MAX];
void Create(int Startx,int Starty,int m)
{ if(m==1) map[Startx][Starty]='X';
if(m==1) return ;
Create(Startx,Starty,m-1); //左上角图形
Create(Startx,Starty+2*mi[m-2],m-1); //右上角图形
Create(Startx+mi[m-2],Starty+mi[m-2],m-1); //中间图形
Create(Startx+2*mi[m-2],Starty,m-1); //左下角图形
Create(Startx+2*mi[m-2],Starty+2*mi[m-2],m-1); //右下角图形
}
int main()
{ while(cin>>n,n!=-1)
{ CLR(map,' ');
Create(0,0,n);
for(int i=0;i<mi[n-1];i++)
{ for(int j=0;j<mi[n-1];j++)
cout<<map[i][j];
cout<<endl;
}
cout<<"-"<<endl;
}
return 0;
}
例题3:poj 2255题目链接:http://poj.org/problem?id=2255
题目大意是给出前序和中序遍历,输出后序遍历。可以使用递归实现:先序遍历(根左右)的第一个字母是根,然后我们可以在中序遍历中找到根的左子树和右子树,例如题目中第一个测试数据:DBACEGF ABCDEFG,我们由先序遍历知D为根结点,由中序遍历知D的左子树为(ABC),右子树为(EFG),同样对D的左右子树进行前序遍历,后进行中序遍历,可知这棵树的结构,然后只需后序遍历输出即可。当然更为简单的方法就是直接在递归中输出后序遍历结果,而可以减少创树的过程。
#include<iostream>
#include<string>
using namespace std;
#define len(s) s.length()
void CreateTree(string s1,string s2)
{ if(len(s1)<=0) return ;
int pos=s2.find(s1[0]); //找到根节点在中序遍历中的位置
CreateTree(s1.substr(1,pos),s2.substr(0,pos)); //根的左子树递归
CreateTree(s1.substr(pos+1,len(s1)-pos),s2.substr(pos+1,len(s1)-pos)); //根的右子树递归
cout<<s1[0]; //遵循后序遍历:左右根
}
int main()
{ string s1,s2;
while(cin>>s1>>s2)
{ CreateTree(s1,s2);
cout<<endl;
}
return 0;
}
例题4:百练2756题目链接:http://poj.grids.cn/practice/2756,简单的题目,还是中文题Nice!!!只写下递归的代码:
int Tree(int x,int y)
{ if(x>y) return Tree(x/2,y);
if(x<y) return Tree(x,y/2);
if(x==y) return x;
}
例题5:百练1664题目链接:http://poj.grids.cn/practice/1664
设f(m,n)表示有m个苹果要放入n个盘中的放法总数,
(1)、假设只有一个盘子或者苹果为0时,那么肯定只有一种放法,即:f(m,1)=1或f(0,n)=1;
(2)、盘子数n>m,那么必定有盘子空着,此时就相当于只有m的盘子一样,即:f(m,n)=f(m,m);
(3)、盘子数n<=m时,当至少有一个盘子空着的时候,f(m,n)=f(m,n-1);(相当于这个时候拿掉一个盘子没有影响);当盘子都不为空时,这个时候相当于从每个盘子中拿掉一个苹果而没有影响,即:f(m,n)=f(m-n,n);由加法原理有:f(m,n)=f(m,n-1)+f(m-n,n)
递归实现的方法:
int f(int m,int n)
{ if(n==1||m==0) return 1;
if(n>m) return f(m,m);
return f(m,n-1)+f(m-n,n);
}
同这种做法的题目还有:足球赛票
题目大意是:有一场很激烈的球赛开始前,会进行售票工作,每张球票为50元,现有2n个人排队等着买票,其中有n个人手中拿着50元的钱,其余n个人拿着100元的钱,开始的时候售票处没有零钱,问这2n个人有多少种排队方式,使售票处不至于找不开钱的情况?
输入:一个非负整数n(1<=n<=1000),输出这2n个人的排队方式,例如输入:3 4输出:5 14
我们同样可设f(m,n)为有m个人拿50元的钱,n个人拿100元 的排队方法总数,
(1)当n=0时,那么这m个人排队的方式就只有一种:即f(m,0)=1;
(2)当m<n时,无论怎么排都会存在找不开钱的情况,即f(m,n)=0;
(3)当m>=n时,有(m+n)个人排队买票,可以假设第(m+n)个人站在第(m+n-1)个人的后面,那么:
①、若第(m+n)个人拿的是100元的,那么在他之前的(m+n-1)个人中必定有m个人拿50元的钱,有n-1个人拿100的,这时有f(m,n-1)种方法
②、若第(m+n)个人拿的是50元的,那么在他之前的(m+n-1)个人中必定有m-1个人拿50元的,有n个人拿100的,这时有f(m-1,n)种方法
由加法原理有:f(m,n)=f(m-1,n)+f(m,n-1)种方法。
=========================================================================================================
Stirling数:包含m个元素的集合划分为正好n个非空子集的方法的数目。
递推式为:f(m,n)=0;(m<n||n=0);f(m,m)=f(m,1)=1;f(m,n)=f(m-1,n-1)+n*f(m-1,n);
分析:设有m个不同 的元素,从中取出某个元素a,那么这个a有两种放法:
①、a自己划分为单个子集,那么其他的m-1元素只能划在n-1个子集中;
②、他和别的球放在同一个盒子,那么只需把其余的m-1个元素放在n个子集中,后将该元素插入到各个盒子中,有k种插法,所以n*f(m-1,n)
=========================================================================================================
递归调用之数的划分:nyist 90,276
例题1:nyist 90题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=90
同上面两例,设f(m,n)为将整数m划分为小于或等于n份的划分总数。
①、当m=1或n=1时,一定只有一种划分方法,即:f(m,1)=f(1,n)=1;
②、当m<n,即需划分的数小于划分的个数,那么就相当于将m划分为m份,即:f(m,n)=f(m,m)(m<n)
③、当m=n时,那么就相当于将m划分为n-1份+1,即f(m,n)=f(m,n-1)+1;(这里不规定划分份数)
其它m>n时,假设未分为n份的话,(因为在这里不规定划分的份数),那么此时减少一份的话仍然不改变结果,即:f(m,n)=f(m,n-1);假设分为n份的话,那么从每一份中拿走一个1也不会对结果造成影响,即:f(m,n)=f(m-n,n),由加法原理有:f(m,n)=f(m,n-1)+f(m-n,n);
#include<iostream>
using namespace std;
int f(int m,int n)
{ if(n==1||m==1) return 1;
if(m<n) return f(m,m);
if(m==n) return f(m,n-1)+1;
return f(m,n-1)+f(m-n,n);
}
int main()
{ int m,n;
cin>>n;
while(n--)
{ cin>>m;
cout<<f(m,m)<<endl;
}
return 0;
}
涉及到递归的题目有POJ 1979(红与黑)--简单的DFS,3768(图形问题----和2083比较的相似),2506(只是用递归思想---分析:题目意思是有两种规格的地板2*1和2*2的地板,要拼成2*n的地板,总共有多少种方法?用a[n]表示,若是拼成了2*(n-1)的地板,最后一块就只能用2*1的地板,若拼成了2*(n-2)的地板,就能用一块2*2的地板或两块2*1的地板,所以递推式为:a[n]=a[n-1]+2*a[n-2],考虑到n的大小,得用高精度。),1145(树的遍历),1941(又是一个画图形的题目),2676(数字九宫格问题--DFS)2790(迷宫问题---DFS),3931(约瑟夫环求人数--递归),1321(棋盘问题,类似于八皇后问题--DFS),2876等.
贴下POJ 3768的代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAX=3010;
#define mi(n,m) (int)pow((double)n,(double)m)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
char graph[10][10],map[MAX][MAX];
int n,m;
void copy(int x,int y)//在(x,y)的位置复制图形
{ for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
map[x+i][y+j]=graph[i][j];
}
void Create(int Startx,int Starty,int num)
{ if(num==1) copy(Startx,Starty);
else
{ for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(graph[i][j]!=' ') Create(Startx+i*mi(n,num-1),Starty+j*mi(n,num-1),num-1);
}
}
int main()
{ while(scanf("%d",&n),n!=0)
{ getchar();
CLR(map,' ');
for(int i=0;i<n;i++)
gets(graph[i]);
scanf("%d",&m);
Create(0,0,m);
for(int i=0;i<mi(n,m);i++)
{ map[i][mi(n,m)]='\0';//使用puts输出时候要注意!!!!
puts(map[i]);
}
}
return 0;
}
POJ 1941代码(仔细点即可,记得每组数据后一个空行)
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAX=2050;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
char map[MAX][MAX];
int n,mi[12]={1,2,4,8,16,32,64,128,256,512,1024,2048};
void Graph(int x,int y)
{ map[x+1][y-1]=map[x][y]='/';
map[x+1][y]=map[x+1][y+1]='_';//注意'_'前后的空格,这个已经可以了~~
map[x][y+1]=map[x+1][y+2]='\\';//'\'的表示法
}
void Create(int Startx,int Starty,int m)
{ if(m==1) Graph(Startx,Starty);
if(m==1) return ;
Create(Startx,Starty,m-1);
Create(Startx+mi[m-1],Starty-mi[m-1],m-1);
Create(Startx+mi[m-1],Starty+mi[m-1],m-1);
}
int main()
{ while(scanf("%d",&n)&&n)
{ CLR(map,' ');
Create(1,mi[n],n);
for(int i=1;i<=mi[n];i++)
{ for(int j=1;j<=mi[n+1];j++)//注意最底下的一行是2^(n+1)个字符
putchar(map[i][j]);
printf("\n");
}
printf("\n");
}
return 0;
}