定义
程序调用自身的编程技巧称为递归。
递归的实质是将问题转化为规模更小的相同问题。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
它的思想类似于下面这个分段函数(斐波那契数列的第x项):
构成递归需具备的条件
- 子问题须与原始问题为同样的事,且更为简单;
- 不能无限制地调用本身,必须有个出口,转变为非递归状况处理。
例题
斐波那契数列
题目:
无穷数列1,1,2,3,5,8,13,21,34,55…称为Fibonacci数列,它可以递归地定义为
现要你来求第n个斐波纳奇数。(第1个、第二个都为1)
思路:
虽然我们可以使用递推法简单快速的求出斐波那契数列,但为了熟悉一下递归,我们将使用递归法解决这个问题。
为了求f(x)的值,我们需要先求出f(x-1)和f(x-2)的值,然后返回它们的和。
当x=1或x=2时,我们就可以直接返回答案1。
用函数去模拟上述过程。
代码:
#include <stdio.h>
int Fibonacci(int x)
{
if (x==1||x==2) return 1;
return Fibonacci(x-1)+Fibonacci(x-2);
}
int main()
{
int x,n;
for (scanf("%d",&n);n--;)
{
scanf("%d",&x);
printf("%d\n",Fibonacci(x));
}
return 0;
}
最大公因数
题目:
给出两个数,求他们的最大公因数。
思路:
不知道大家是否了解欧几里得算法。
我们约定gcd(x,y)表示x和y的最大公因数(x>y),欧几里得算法表明,gcd(x,y)=gcd(y,x%y)。
下面对该式进行证明:
令ans=gcd(x,y),
则x=mans,y=nans(m和n均为正整数)。
所以x-y=(m-n)ans。
因为ans为x和y的最大公因数,
所以gcd(m,n)=1。
若m-n和n不互质,则有公约数K,且存在正整数p,q,使m-n=pK,n=qK。
所以m=(m-n)+n=pK+qK=(p+q)K,说明K是m和n的公约数,这与题意条件【m和n互质】矛盾。
所以gcd(n,m-n)=1。
所以gcd(mans,(n-m)*ans)=ans=gcd(y,x-y)。
又因x%y=x-⌊x/y⌋ *y,
所以gcd(x,y)=gcd(y,x%y)。
用此方法即可快速缩减x和y的范围。当x恰为y的整数倍时,返回答案y。易证用此方法求gcd的时间复杂度为O(log(x+y))。
代码:
#include <stdio.h>
int gcd(int x,int y)
{
if (x%y==0) return y;
return gcd(y,x%y);
}
int main()
{
int x,y,n;
scanf("%d%d",&x,&y);
printf("%d\n",gcd(x,y));
}
汉诺塔
题目:
有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子B上,并且任意时刻都不能出现大盘子在小盘子上方的情况,请问至少需要移动多少次才能将n个圆盘全部转移到C柱子上。
思路:
将A柱子上的圆盘转移到C柱子上的问题可以转化为:先将n-1个圆盘转移到B柱子上,再将第n个圆盘转移到C柱子上,再将n-1个圆盘转移到C柱子上。
代码:
#include <stdio.h>
//返回把n个盘子从a柱子全部移动到c柱子的方案数
long long qwq(int n,int a,int b,int c)
{
if (n==1) return 1;
return qwq(n-1,a,c,b)+1+qwq(n-1,b,a,c);
}
int main()
{
int n;
scanf("%d",&n);
printf("%lld",qwq(n,1,2,3));
}
优化:
可以发
现我们进行了过多的重复计算,比如,把n个盘子从a柱子移动到b柱子和把n个盘子从c柱子移动到b柱子没有任何区别。
由此,令a[n]表示把n个盘子由某个柱子移动到另一个柱子的方案数,我们就可以得到一个简单的递推式:a[n]=a[n-1]*2+1。
由高中数学等比数列知识可得:a[n]=2^n-1
快速幂
题目:
求a的b次方对p取余的结果。
a,b,p不超过maxint。
思路:
一个性质:aaa%p=a%pa%pa%p。
一些说法:a模p,a对p取余,a除以p取余(是一个意思)。
循环b次乘a会超时(c语言自带的函数也会超时)。
由幂的运算性质可得:
我们可以通过这一性质快速计算a的b次方。
代码:
#include <stdio.h>
long long ksm(long long a,long long b,long long p)
{
if (!b) return 1%p;
if (b%2) return ksm(a*a%p,b/2,p)*a%p;
return ksm(a*a%p,b/2,p);
}
int main()
{
long long a,b,p;
scanf("%lld%lld%lld",&a,&b,&p);
printf("%lld^%lld mod %lld=%lld\n",a,b,p,ksm(a,b,p));
}