前言
这是本人写的第一篇总结帖,花了很久的时间写得也很烂,希望大家勿喷……
Update on 2021/7/16 \text{Update on 2021/7/16} Update on 2021/7/16 :话说我的第一篇博客原来这么弱智吗……还有后面的估计除了 dfs 都要咕了 qwq(bushi
各位在座的在站的在躺的在趴的以及以各种奇奇怪怪的姿势出现在电脑前的大佬们大家好,虽然我知道你们都是大佬,但是这篇博客,我还是会把你们当成萌新来讲解的((
好了废话不多说,下面进入正题
递推
从已知道的若干项出发,利用递推关系依次推算出后面的未知项的方法,我们称为递推算法。
做递推算法最关键的是找出递推式,然后再求出最简单的情况的值并存在数组里。
这个要直接求出值的情况个数要根据递推式来看。
比如说,如果一个递推式是f(i)=f(i-3)+f(i-2),那么我们需要把1、2、3的情况都先写出来,然后从4开始循环。
递推模板:
#include<cstdio>
const int MAXN=...;
int a[MAXN]={
需要存的最简单的情况};
int main(){
int n;
scanf("%d",&n);
for(int i=...;i<=n;i++){
递推式;
}
printf("%d",a[n]);
}
递归
从已知问题的结果出发,用迭代表达式逐步推算出问题的开始的条件,即顺推法的逆过程,称为递归。
当然这是算法的解释了,递归其实就是套娃函数自己调用自己,只不过比纯粹的套娃要难 1 0 8 10^8 108些罢了((。
从算法的角度来说,显然无限套娃是没有意义的,所以我们也需要一个递归的出口。
题外话:其实后面学习的很多算法中都有递归思想,所以递归很重要!
因为递归变化较多,模板不太好写,就不写了。
区别与联系
之所以把递推和递归放在一起讲说明他们是有共同点的,根据本人为数不多的做题经验(一定要多刷题啊!awa),可以发现递推和递归都是要推出在一个规律下,不同值对应的不同结果之间的关系,从而推出答案的值,也都需要一个出口来结束程序。
m j l mjl mjl:而递推与递归的不同之处在于,递归是从未知到已知,逐步接近解决问题的过程,而递推从已知到未知。
翻译成人话就是:递推是从小到大一点一点推,而递归是一个栈的结构——先从最终结果出发,一点一点往前推,直到推到出口,再根据出口的数值把答案推出来。
最直观的图:
(↑以f(x)=f(x-1)+x且f(1)=1举例,求f(5),递推与递归的区别与联系)
递推递归的5种模型
递推递归有一些模型,这些模型可以在递推和递归中通用。
1.斐波那契数列(Fibonacci)
这是一种很简单的递推模型。
这种模型一般都是,此时第x项数据与前面的数据有直接的数值关联(一般来说是很明显的倍数关系)。【能理解那个意思就行】
例题-铺砖1
题目描述
有 2 ∗ n 2 * n 2∗n的一个长方形方格道路,只有一种1$* $2的砖去铺,总共有多少种铺法呢?
输入格式
一行,一个 n n n( 0 ≤ n ≤ 45 0≤n≤45 0≤n≤45)
输出格式
一行,一个数(总共有多少种铺法)
样例输入
3
样例输出
3
很明显可以看到末尾的砖块(只是末尾)有两种放法:
1.竖着放一块;
2.横着放两块。
我们在放最新的第i列,即末尾的砖块时,这两种放法对应着 i − 1 i-1 i−1和 i − 2 i-2 i−2的情况,如下图。
所以可以看出这是一道很经典的Fibonacci的题目。
递推式: a [ i ] = a [ i − 1 ] + a [ i − 2 ] a[i]=a[i-1]+a[i-2] a[i]=a[i−1]+a[i−2]
80分代码(很简单):
#include<cstdio>
int f(int n){
if(n==1)return 1;
if(n==2)return 2;
else return f(n-1)+f(n-2);
}
int main(){
int n;
scanf("%d",&n);
printf("%d",f(n));
return 0;
}
至于为什么是80分,这里先不说,看到后面就知道了。
2.汉诺塔(Hanoi)
汉诺塔本身的问题是把若干从小到大堆叠的圆盘借助一根辅助的柱子从一根柱子移到另一根柱子上,要求一次只能移动一个,而且大的不能压在小的上面。
我们研究最原始的问题。
例题-汉诺塔问题
题目描述
1、一次只许移动一个盘
2、任何时候、任何柱子不允许把大盘放在小盘上面。
3、可使用任一一根立柱暂存圆盘。
问:如何使用最少步数实现n个盘子的移动?打印出具体移动方案。
输入格式
一行一个数n, 1<= n <= 18
输出格式
输出若干行,第i行表示第i步的移动方案。具体格式参见输出样例。
样例输入
3
样例输出
A->C
A->B
C->B
A->C
B->A
B->C
A->C
我们可以假设这三根柱子分别为 A A A, B B B, C C C,我们把 n n n个盘子从 A A A借助 B B B移到 C C C上。
如果只有一个盘子,我们就直接移。
如果有两个以上的盘子,我们就分3步做:
1.把上面 n − 1 n-1 n−1个盘子从 A A A借助 C C C移到 B B B上,腾出 C C C的位置;
2.把留在 A A A上的最大的圆盘移到 C C C上;
3.把暂时放在 B B B上的其他圆盘从 B B B借助 A A A移到 C C C上。
而第一步和第三步怎么移过去,就需要继续把这个问题分解成最大的和其他的问题,直到只剩下一个圆盘,就直接移过去。
递推式(只求步数不求过程):$ a[i]=2 * a[i-1]+1 $
AC代码:
#include<cstdio>
//n:圆盘数量,a、b、c:柱子编号
void h(int n,char a,char b,char c){
if(n==0)return;
h(n-1,a,c,b); //第一步
printf("%c->%c\n",a,c); //第二步
h(n-1,b,a,c); //第三步
}
int main(){
int n;
scanf("%d",&n);
h(n,'A','B','C');
return 0;
}
3.平面分割
虽然我不知道这个模型有什么用但是既然老师讲了我就把它写上去吧
用一些两两相交但是不会三个及以上相交的圆把平面分成若干个区域。
观察答案,我们会发现:
a n s [ 2 ] − a n s [ 1 ] = 2 ; ans[2]-ans[1]=2; ans[2]−ans[1]=2;
a n s [ 3 ] − a n s [ 2 ] = 4 ; ans[3]-ans[2]=4; ans[3]−ans[2]=4;
a n s [ 4 ] − a n s [ 3 ] = 6 ; ans[4]-ans[3]=6; ans[4]−ans[3]=6;
… … …… ……
很明显的差等差数列。发现这个之后规律就出来了:
递推式: a [ i ] = a [ i − 1 ] + 2 ∗ ( i − 1 ) a[i]=a[i-1]+2*(i-1) a[i]=