递归函数
直接或者简介调用自身的函数成为递归函数。
它通常把一个大型复杂的问题层层转化为一个为一个与原问题相似的规模较小的问题来解决。将一个由重复操作的大问题分解开来。
递归函数的关键在于寻找递归定义和递归终止条件。
递归定义:使问题向边界条件转的规则。递归定义必须能够使问题越来越简单。
递归终止条件:也就是所描述问题的最简单情况,它本身不再使用递归的定义。
但是,有时针对问题分析不到位,反而有可能使得问题更加麻烦。
例如装箱问题:
这个问题可以分解为对于一个箱子,由大到小装进这一个箱子。
如果不能装进,寻找下一个东西。但是这样会对一个实际问题步骤,重复自身,导致时间超时。
对于以下这个函数,问题就是当数据较大时,将直接超时。
#include<bits/stdc++.h>
using namespace std;
int bs,e[7],n;
int zi(int,int,int);
int main()
{int s;s=0;
while(cin>>e[1]>>e[2]>>e[3]>>e[4]>>e[5]>>e[6])
{bs=0;if(e[1]==0&&e[2]==0&&e[3]==0&&e[4]==0&&e[5]==0&&e[6]==0)break;
else{
bs=e[6];e[6]=0;
zi(36,5,0);}
printf("%d\n",bs);
}}
int zi(int x,int y,int z)
{if(y==1&&e[1]==0&&x==36){return 0;}
if(x!=36&&y==1&&e[1]==0)
{bs++;
return zi(36,5,0);
}
else
{if(e[y]!=0)
{if(x-y*y>=0&&z+y<=6)
{e[y]--;z=y;
return zi(x-y*y,y,z);
}
else return zi(x,--y,z);}
else {
return zi(x,--y,z);}
}
}
递归算法分析
例:求N!
在其中,找出函数调用自己的条件,状态传递方法。
另一个,找出终止条件,及为当阶乘算到1时停止。
函数如下:
fun(int n)
{if(i==1||i==0)return 1;//终止条件
else return n*fun(n-1);//函数自身调用方式
}
课件 集合的全排列问题
设有由n个元素组成的集合,对其进行全排列,则共有n!种方法。
令 R1=R-{r1}。集合中的全排列标记为perm(x),则(r1)perm(x)表示在全排列perm(x)
的perm(x)的每一个排列前加一个前缀r1的排列。
R的全排列可归纳定义如下;
当 n=1时perm(R)=(r),其中r 是集合R中唯一的元素;
当n>1s时 则为在一个已经拍好的集合前面加上这个元素。
void perm(int list[],int k,int m)
{if(k==m)//所有数已经排列完
{for(int i=0;i<=m;i++)
cout<<list[i]<<" ";//输出组合好的排列
cout<<endl;
}
else
for(int j=k;j<=m;j++)//先排第一个元素,接着第二个,接着往下依次排元素。
{swap(list[k],list[j]);//但是当这个元素排完之后,需要归位,方便下面的元素排位。
perm(list,k+1,m);
swap(list[k],list[j]);
}
}
由此可以得到某个范围内的全排列数量。
课件:计算半数集问题的递归算法
int comp(int n)
{int ans=1;
if(n>1)for(int i=1;i<=n;i++)//如果有小于一半的数,则进行探索。
ans+=comp(i);
returnn ans;
}
但是 这个函数美中不足的地方为,同一个节点重复计算。
以下为改进的函数
int a[1001];
int comp(int n)
{ int ans=1;
if(a[n]>0)return a[n];//假如这个数组用来存储已经得到的数量。?
for(int i=1;i<=n;i++)
ans_=comp(i);
a[n]=ans;//保存结果
return ans;
}
函数可以自己调用自己,也可以调用其他函数,再由其他函数反过来调用自己。
题目:计算从1加到n的总值。
#include<iosstream>
using namespace std;
int fun(int );
int main()
{int t;
cin>>t;
cout<<fun(t);
}
int fun(int x)
{if(x==1)return 1;//递归终止条件。g
return(fun(n-1)+n);//这一句将返回这个函数以及之前的总值。
}
递归函数,其中使用了 地推关系,以下有几种地推关系
1。为斐波那契数列:
问题:有雌雄两只兔子,假定过两个月便可繁殖一对雌雄小兔子,问经过n月一共有几只兔子。
这个问题 可以看成 当月兔子数量由前一个月的老兔子组成和前俩个月的老兔子生的兔子组成
因此具有关系方程式 f(n)=f(n-1)+f(n-2)
2。hanoi塔问题
问题;hanoi塔由n个大小不同的圆盘和三根木柱a,b,c组成。开始时,由大到小依次放在木柱上。
要求,把a住上的木盘放到c柱上。
由 转移方式可以看出,将n个木盘转移到c柱上需要先讲n-1个木盘转移到b柱,再讲第n个放到c柱再讲n-1个转移到c柱上。
h(n)=2*h(n-1)+1
3。
第二类stirling数
定义 n个有区别的球放到m个相同的盆子中,y要求无空盘子,其不同的方案,用S(n,m)
表示,成为第二类stirling数。
解:设有n个不同的球,。从中取出一个球,bn的方法有两种,
1:bn独自占一个盒子,那么其他的球只能放在m-1个盘子中,方案数S2(n-1,m-1):
2:bn与别的球放在一个盒子中,那么可以事先吧前n-1个球放在m个盒子中,然后将球bn放入其中一个盒子中,方案数为mS2(n-1,m)。
由上 S2(n,m)=mS2(n-1,m)+S2(n-1,m-1)
边界可以由定义推导出;
S2(n,o)=0;S2(n,1)=1;S2(n,n)=1;S2(n,k)=0;(k>n).
直接或者简介调用自身的函数成为递归函数。
它通常把一个大型复杂的问题层层转化为一个为一个与原问题相似的规模较小的问题来解决。将一个由重复操作的大问题分解开来。
递归函数的关键在于寻找递归定义和递归终止条件。
递归定义:使问题向边界条件转的规则。递归定义必须能够使问题越来越简单。
递归终止条件:也就是所描述问题的最简单情况,它本身不再使用递归的定义。
但是,有时针对问题分析不到位,反而有可能使得问题更加麻烦。
例如装箱问题:
这个问题可以分解为对于一个箱子,由大到小装进这一个箱子。
如果不能装进,寻找下一个东西。但是这样会对一个实际问题步骤,重复自身,导致时间超时。
对于以下这个函数,问题就是当数据较大时,将直接超时。
#include<bits/stdc++.h>
using namespace std;
int bs,e[7],n;
int zi(int,int,int);
int main()
{int s;s=0;
while(cin>>e[1]>>e[2]>>e[3]>>e[4]>>e[5]>>e[6])
{bs=0;if(e[1]==0&&e[2]==0&&e[3]==0&&e[4]==0&&e[5]==0&&e[6]==0)break;
else{
bs=e[6];e[6]=0;
zi(36,5,0);}
printf("%d\n",bs);
}}
int zi(int x,int y,int z)
{if(y==1&&e[1]==0&&x==36){return 0;}
if(x!=36&&y==1&&e[1]==0)
{bs++;
return zi(36,5,0);
}
else
{if(e[y]!=0)
{if(x-y*y>=0&&z+y<=6)
{e[y]--;z=y;
return zi(x-y*y,y,z);
}
else return zi(x,--y,z);}
else {
return zi(x,--y,z);}
}
}
递归算法分析
例:求N!
在其中,找出函数调用自己的条件,状态传递方法。
另一个,找出终止条件,及为当阶乘算到1时停止。
函数如下:
fun(int n)
{if(i==1||i==0)return 1;//终止条件
else return n*fun(n-1);//函数自身调用方式
}
课件 集合的全排列问题
设有由n个元素组成的集合,对其进行全排列,则共有n!种方法。
令 R1=R-{r1}。集合中的全排列标记为perm(x),则(r1)perm(x)表示在全排列perm(x)
的perm(x)的每一个排列前加一个前缀r1的排列。
R的全排列可归纳定义如下;
当 n=1时perm(R)=(r),其中r 是集合R中唯一的元素;
当n>1s时 则为在一个已经拍好的集合前面加上这个元素。
void perm(int list[],int k,int m)
{if(k==m)//所有数已经排列完
{for(int i=0;i<=m;i++)
cout<<list[i]<<" ";//输出组合好的排列
cout<<endl;
}
else
for(int j=k;j<=m;j++)//先排第一个元素,接着第二个,接着往下依次排元素。
{swap(list[k],list[j]);//但是当这个元素排完之后,需要归位,方便下面的元素排位。
perm(list,k+1,m);
swap(list[k],list[j]);
}
}
由此可以得到某个范围内的全排列数量。
课件:计算半数集问题的递归算法
int comp(int n)
{int ans=1;
if(n>1)for(int i=1;i<=n;i++)//如果有小于一半的数,则进行探索。
ans+=comp(i);
returnn ans;
}
但是 这个函数美中不足的地方为,同一个节点重复计算。
以下为改进的函数
int a[1001];
int comp(int n)
{ int ans=1;
if(a[n]>0)return a[n];//假如这个数组用来存储已经得到的数量。?
for(int i=1;i<=n;i++)
ans_=comp(i);
a[n]=ans;//保存结果
return ans;
}
函数可以自己调用自己,也可以调用其他函数,再由其他函数反过来调用自己。
题目:计算从1加到n的总值。
#include<iosstream>
using namespace std;
int fun(int );
int main()
{int t;
cin>>t;
cout<<fun(t);
}
int fun(int x)
{if(x==1)return 1;//递归终止条件。g
return(fun(n-1)+n);//这一句将返回这个函数以及之前的总值。
}
递归函数,其中使用了 地推关系,以下有几种地推关系
1。为斐波那契数列:
问题:有雌雄两只兔子,假定过两个月便可繁殖一对雌雄小兔子,问经过n月一共有几只兔子。
这个问题 可以看成 当月兔子数量由前一个月的老兔子组成和前俩个月的老兔子生的兔子组成
因此具有关系方程式 f(n)=f(n-1)+f(n-2)
2。hanoi塔问题
问题;hanoi塔由n个大小不同的圆盘和三根木柱a,b,c组成。开始时,由大到小依次放在木柱上。
要求,把a住上的木盘放到c柱上。
由 转移方式可以看出,将n个木盘转移到c柱上需要先讲n-1个木盘转移到b柱,再讲第n个放到c柱再讲n-1个转移到c柱上。
h(n)=2*h(n-1)+1
3。
第二类stirling数
定义 n个有区别的球放到m个相同的盆子中,y要求无空盘子,其不同的方案,用S(n,m)
表示,成为第二类stirling数。
解:设有n个不同的球,。从中取出一个球,bn的方法有两种,
1:bn独自占一个盒子,那么其他的球只能放在m-1个盘子中,方案数S2(n-1,m-1):
2:bn与别的球放在一个盒子中,那么可以事先吧前n-1个球放在m个盒子中,然后将球bn放入其中一个盒子中,方案数为mS2(n-1,m)。
由上 S2(n,m)=mS2(n-1,m)+S2(n-1,m-1)
边界可以由定义推导出;
S2(n,o)=0;S2(n,1)=1;S2(n,n)=1;S2(n,k)=0;(k>n).