递推与递归(用不正经的故事讲正经的知识)

警告:此文章为原创,转载需标明出处

提示:纯手打,难免出错,找到了问题的可以在在评论区指出,谢谢

叠甲叠完了,让我们开始吧

递归

1. 什么是递归

瞧这就是递 "龟",写完了!

彩色静态条

开个小玩笑啊,首先要知道什么是递归,我先给大家讲个故事:

从前有座山,山里有座庙,庙里有一个老和尚和一个小和尚,老和尚再给小和尚讲一个故事: 从前有座山,山里有座庙,庙里有一个老和尚和一个小和尚,老和尚再给小和尚讲一个故事: 从前有座山,山里有座庙,庙里有一个老和尚和一个小和尚,老和尚再给小和尚讲一个故事:...

这个故事相信大家肯定不陌生,但要知道什么是递归,还有要分析其中的道理。

那这个故事的特点是什么呢,就是在故事再次提到相同的故事,这就是递归的核心理念了。

回到编程领域,我们知道一个函数是可以调用另一个函数的,也就是 函数A->函数B,作为特例,如果函数调用了自己,就跟之前的故事一样,也就是 函数A->函数A,我们把函数调用自己的情况叫做递归。

  

现在我们来举个最简单的例子说明。

#include <bits/stdc++.h>
using namespace std;
int f(int x){
    return x+f(x-1);
}
int main(){
    cout<<f(3);
    return 0;
} 

通过这个程序,我们来分析一下 f(3) 的值到底是什么

到这里我们会发现一个问题,每次函数都会无条件的调用自己,这种调用似乎永远不会停止,这显然不是我们想要的,所以我们需要在函数中添加一个判断语句,已决定何时停止调用自己,这个例子里,我们就把参数等于 0 作为终止条件。

#include <bits/stdc++.h>
using namespace std;
int f(int x){
    if(x>0){
        return x+f(x-1);
    }else{
        return 0;
    }
}
int main(){
    cout<<f(3);
    return 0;
}

那输出为什么是 6 呢?

我们模拟一下程序的执行过程就明白了:

f(3) = 3+f(2)\\ = 3+2+f(1)\\ =3+2+1+f(0)\\ =3+2+1+0\\ =6

像这样用判断语句把函数终止掉,这种情况就是递归的终止条件。

那用递归如何求解 1 一直加到 100 呢?

其实很简单,把上面的 f(3) 换成 f(100) 就行了。

#include <bits/stdc++.h>
using namespace std;
int f(int x){
    if(x>0){
        return x+f(x-1);
    }else{
        return 0;
    }
}
int main(){
    cout<<f(100);
    return 0;
}

那为什么结果为 5050 呢,因为我们上次传的参数是 3 结果为 3+2+1+0, 如果把参数换成 100,他就会从 1 一直加到 100 了。

可能有人会比较好奇,如果不停止递归会怎么样

 好了请给出答案:

揭秘答案:选2 

那为什么把判断语句去掉就会错误呢,因为程序运行时,调用函数是有代价的,要占用一片叫做“栈(stack)”的内存空间,调用函数时,数据会保存栈里。

结束时,数据就会被取出,可想而知,如果调用很多函数都不返回,数据就没地方放了,这种错误就叫做栈溢出

但是最后要注意递归与循环不同,递归的第 2 件事是在第一件事结束前发生的,而循环是在第一件事结束后发生的。

2.习题讲解

题目描述

用递归的方法求 1+2+3+ ... +N 的值。

输入格式

输入 N

输出格式

输出和。

输入输出样例

输入 #1

5

输出 #1

15
说明/提示

N\le 200

题解:

递归最重要的有两个东西:边界调用下一层递归

这道题的边界和如何调用下一层递归并不难找,我们就可以很轻松地打出如下递归函数:

int sum(int x){
	if(x==1) return 1;//边界
	else return x+sum(x-1);//调用下一层递归
}

这题毕竟是道简单题,主函数也好打。

完整代码:
#include<bits/stdc++.h>//万能头
using namespace std;
int sum(int x){//递归函数
	if(x==1) return 1;
	else return x+sum(x-1);
}
int main(){
	int n;
	scanf("%d",&n);
	printf("%d",sum(n));
	return 0;
}

这道题其实还有 O(1)算法,要用到小学知识等差数列求和公式。

代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	scanf("%d",&n);
	printf("%d",(1+n)*n/2);//套用公式
	return 0;
}
2.题目描述

阿克曼 (Ackmann) 函数 A(m,n) 中,m,n 定义域是非负整数 (m≤3,n≤10),函数值定义为:

akm(m,n)=n+1;(m=0 时 )。

akm(m,n)=akm(m-1,1);(m>0,n=0 时 )。

akm(m,n)=akm(m-1,akm(m,n-1));(m,n>0 时 )。

输入格式

m 和 n。

输出格式

函数值。

输入输出样例

输入 #1复制

2 3

输出 #1复制

9

题解:

递归调用函数则是核心,每一个答案都是从其他的答案推导过来的。

如果我们往这个函数里传入一个参数 5,这个函数会发生像这样的事情:

fib(5)\\ =fib(4)+fib(3)\\ =(fib(3)+fib(2))+(fib(2)+fib(1))\\ =((fib(2)+fib(1))+1)+(1+1)\\ =((1+1)+1)+2\\ =5

每一项都是由前两项推导而成,这就是递归的一种简单的使用方法。

代码
#include <bits/stdc++.h>
 
using namespace std;
 
int a, b;
int ack(int a, int b){
    if (a == 0) return b + 1;
    if (b == 0) return ack(a - 1, 1); // 递归边界
    return ack(a - 1, ack(a, b - 1)); // 函数递归调用
}
int main(){
    cin >> a >> b;
    cout << ack(a, b);
}

总结

  1. 递归:函数自己调用自己
  2. 只递不归会导致程序崩溃
  3. 要在适当的时候终止递归

递推

要搞懂递推的话,我们先看一个问题

从前有一只快乐的小鸡,他呢有一个神奇的力量,那就是——永远都不会死。当然他的儿孙也不会死,小鸡两天长成大鸡,大鸡每一个月都生一只小鸡,这时问题来了,一年后有多少鸡。

这是你可能还是想用上述方法解,但是,不可能,因为,会超时。

那为什么会超时呢,因为递归的时间复杂度是指数级增长,那这时候我们就要考虑正解 —— 递推了。

1.什么是递推

  •  就是用重复的运算来推出最终答案的算法

是不是很简单,那还不来到题练练手 题目传送门

问题描述:

给定一个小孩,让他上楼梯,他不上。

 用n阶台阶,小孩一次可以上k级台阶,答案对 100003 取模

这道题不同于一般的斐波那契数列,我们需要推导(瞎猜)一下公式。

原版公式:

F_i=F_{i-1}+F_{i-2}(i>2)

F_i=1(i=1 or i=2)

这种斐波那契数列可以解决一下问题

有N级的台阶,你一开始在底部,每次可以向上迈2级,1级台阶,问到达第N级台阶有多少种不同方式。

这种问题我们的第i个台阶,只能从第i-1个台阶和第i-2个台阶上走来,所以公式为上。

反过来看这道题目:

有N级的台阶,你一开始在底部,每次可以向上迈最多K级台阶,问到达第NN级台阶有多少种不同方式。

我们的第i个台阶,就可以从第i-1,i-2到i-k个台阶登陆 (i≥k),这就好办了

F_i=\displaystyle \sum^{t=min(i,k)}_{t=1}{F_{i-t}}

程序如下:

#include<bits/stdc++.h>
using namespace std;
int f[100005],n,k;
int main()
{
	cin>>n>>k;
	f[0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=min(i,k);j++)
			f[i]=(f[i]+f[i-j]);
	}
	cout<<f[n];
	return 0;
} 

提交,咦,怎么错了,等等我的取模呢?

 结果当然是AC的啦

#include<bits/stdc++.h>
using namespace std;
int f[100005],n,k;
int main()
{
	cin>>n>>k;
	f[0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=min(i,k);j++)
			f[i]=(f[i]+f[i-j])%100003;
	}
	cout<<f[n];
	return 0;
} 

总之,递推就是有一些题型是让你自己找数字中的规律,进而找出递推关系式,而还有些题是告诉你了递推关系式,让你自己去地推。但不管怎样,碰到递推的题一定要首先找到递推关系式,再解答。

我写了怎么多能不能给我个赞呢!谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值