本集为普通版,由于这个是在初学递推时所写,虽然多,思路还行,但是就是没有逻辑感,对递推还没到精髓的程度,所以作者会再出一集详细版,为读者整理好思路,然后更加有逻辑的去理解递推为何物,如何去理解递推这个思想!
递推在c语言学习中是一个十分关键的思想,我们最初的递推可以源于数学的全排列,递推规律(数列)甚至脑筋急转弯(高考题中也有递推的数列题(emm,概率和统计那块(全国卷为例))),而这里整理了一些递推 经典题,目的是帮助我自己理清递推的思路,如有侵权,请联系我!!
经典例题1
题目描述
爬楼梯(简单版1)(入门)
欧老师爬楼梯,他可以每次走 11 级或者 33 级,输入楼梯的级数,求不同的走法数。
例如:楼梯一共有 3 级,他可以每次都走一级,或者一次走三级,一共 2 种方法。
输入格式
输入包含若干行,每行包含一个正整数 N,代表楼梯级数,1<=N<=30。
输出格式
不同的走法数,每一行输入对应一行输出。
样例1
输入
1
3
5
输出
1
2
4
很好,那么我们很快能while(scanf("%d",&a[i])!=EOF)来处理多组输入的数据,再用一个循环来输出,那么问题在于怎么去算出这个递推!!!
我们看到这道题其实难度比较难(较初学者来说,可以先去学一下跨1和2阶的),我们学习1和2阶的思路,我们找到F(n)=F(n-1)+F(n-3)的递推式(我们假设我们到了最后一步,那么我们可以跨1步或3步来结束爬楼梯),那么就可以仿照写出oy(int n)的函数,不过请注意,这里以 2为例,我们要考虑所有出口即为n==1或者n==3,那么4就是3和1,5是2和4,那这个2我们没定义呀,我们知道2是1种方法(2个1步),那么就可以写出下面的代码!!
//递推爬楼梯
#include<stdio.h>
int oy(int n)//重点在这个函数
{
if(n==1||n==2)//浓缩了一下
return 1;
if(n==3)
return 2;
else
return oy(n-1)+oy(n-3);//我的建议是以特殊情况举例,比如4(3和1),5(4和2)那就出口必须有123
}
int main()
{
int a[1000],i=0;
while(scanf("%d",&a[i])!=EOF)
{
a[i]=oy(a[i]);
i++;
}
for(int j=0;j<i;j++)
{
printf("%d\n",a[j]);
}
return 0;
}
例题2 全排列
题目描述
输入一个数 N, 求 1到 N 的全排列。
输入格式
输入一个 N,N<12。
输出格式
输出 N 的全排列,每个数字占 4
个位置。
样例1
输入
3
输出
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
那么这道题拿到手,我们初学者可能就直接写几个循环就敷衍了事,甚至有的人都不知道在说什么,我们先简单点,只用打出他的组数,那么我们按我们前面的例题思路(可以这么写)
#include<stdio.h>
int oy(int n)
{
if(n==1)
return 1;
else if(n==2)
return 2;
else
return n*oy(n-1);
}
int main()
{
int a;
scanf("%d",&a);
printf("%d",oy(a));
}
但其实我们可以学习一个新的概念深度优先搜索(DFS),emm,不耍流氓先说概念:
深度优先搜索
深度优先搜索 (Depth-First Search,DFS)是一种常见的图搜索算法,它可以用于遍历或搜索树或图的数据结构。深度优先搜索从根节点开始,沿着一条路径一直搜索下去,直到到达最深的节点或无法继续搜索为止,然后回溯到上一个节点,继续搜索其他路径,直到所有节点都被访问过为止。
emm,读者可以去对比广度优先搜索(BFS),这里赘述!!
深度优先搜索算法的步骤如下:
- 从起始节点开始搜索,将其标记为已访问。
- 沿着一条路径一直搜索下去,直到到达最深的节点或无法继续搜索为止。
- 回溯到上一个节点,继续搜索其他路径,直到所有节点都被访问过为止。
深度优先搜索算法的应用非常广泛,例如在迷宫问题、拓扑排序、连通性问题、最短路径问题等方面都有应用。
具体做法你可以看一下这篇博客:DFS入门讲解
所以腻,为了方便读者理解,我也写一下(较为通俗的代码)
#include<stdio.h>
int p[10000]={0},l[1000]={0};
int k;
void oy(int n)//如果把这个排列看成盒子,那n就是你走到第几个盒子了
{
if(n==k+1)//走到第k+1个盒子则立马结束,
{
for(int i=1;i<=k;i++)
printf("%d ",p[i]);//立马打印这一行
printf("\n");
return;//结束
}
for(int i=1;i<=k;i++)
{
if(l[i]==0)
{
p[n]=i;//将号码放到盒子里
l[i]=1;//标记他被使用了
oy(n+1);//走到下一个盒子,123或者132;然后走到最后一个盒子,return,之后1的排列结束,所有的数字变成都没被使用,继续2
l[i]=0;
}
}
return;
}
int main()
{
scanf("%d",&k);
oy(1);
return 0;
}
例题三:成群的奶牛
题目描述
现有 n 只奶牛在一片宽广的草地上吃草,草地有一些小路,可以去到其他草场。这些小路都是单向的。
每个草场有 2 条小路可以去到到其他草场,但是只有 1条小路可以到达这个草场。
奶牛从其中一个草场开始,每遇到一个草场,奶牛们会精确地分成两群,这两群奶牛数量之差的绝对值为 k,分别从两条路去到下一个草场(如果数量不满足要求就不会再分裂)。
当来到一个草场而该奶牛群已不可再分割时,奶牛们就会停下来在这里吃草。
现给出 n 和 k,求奶牛最终会分成多少群。
输入格式
一行,分别是 n 和 k,用空格隔开。
输出格式
一个正整数,表示最终奶牛会分成多少群。
样例1
输入
50 4
输出
2
样例2
输入
49 1
输出
6
数据规模与约定
0<n≤10000
0<k<n
那么我们知道,递推嘛,肯定先找出口,这题我们可以举个例子,比如样例的k=4;我们 选用8为例子 (如下丑图)
我们不难得出,每次分裂后的两个数为(n-k)/2和(n+k)/2,不满足的条件要么现在这个数比k小,要么分解后是分数(这个怎么去表示?(分数用相加不等于原数表示))
//成群的奶牛
#include<stdio.h>
int a,b;
int o=1;//o代表有几群奶牛
void oy(int n)
{
int p=(n-b)/2;//格外注意这里的int不能提到函数外面,第一,这个是函数使用时产生的一个临时参数,放外面就变成了占内存的实际参数了
int l=(n+b)/2;//先定义两个分裂数出来
if(p+l!=n)
return;//不返回值(定义需为void),用于跳出
o++;//这里建议去画图体会一下o数量的变化
if(p>b)
oy(p);
if(l>b)
oy(l);
}
int main()
{
scanf("%d %d",&a,&b);
oy(a);
printf("%d",o);
return 0;
}
小小结
写到这里我们发现了例一中的return oy(n-1)+oy(n-3)起到了一个sum的作用,其实就是将其全部计算出来后相加,比如要oy(n-3)那就把那n-3递推,而在这个奶牛中我们定义了一个o计数器,emm,区别就在于1.我们不返回值,2.我们只要知道次数就行,本质就是他不满足f(n)=f(n-k)+f(n-j)类,而是一种条件类!!!!
例四:初级快速幂训练
题目描述
请你计算a
的n
次方是多少,由于答案可能会很大,所以你只需要输出答案和b
取余数的结果。
输入格式
多组输入,EOF
结束。
每组一行,输入三个非负整数分别表示 a,n,b
,(a,b,n
均在int
范围内)。
输出格式
对于每一组输入,在新的一行输出结果。
样例1
输入
2 10 10
3 3 5
输出
4
2
看完前四例后我们提高一下 难度,体会一下递推的神奇之处,也给大家一种新的求幂的算法
//初级快速幂(分治或者递归思想)
#include<bits/stdc++.h>//c++的头文件
using namespace std;
long long oy(long long a, long long n, long long b)//二分法递归快速幂(其实就是分治算法,但是我喜欢称之为递推)
{//首先这是一个计算a的n次方的函数(记住哦)
if (n <= 1) //递归出口设置为当指数为1
//当n被分解到1或者等于0(这里只考虑者两种情况)
{ //0去分解答案为1
if (n == 0)
return 1 % b;
else //1答案为a的1次幂去%b
return a % b;
} //理解这里的递推!!
long long half = n / 2; //二分法开始了(一半赋给half)
long long tem= oy(a, half, b); //计算少一半次数的a的n/2次方==临时tem
if (n % 2 == 0) //对指数操作:
{
return (tem * tem) % b; //能被2整除即可以用tem*tem求出所有
}
else
{
return (tem * oy(a, half + 1, b)) % b;//不能的话就是乘多一项(int会砍掉小数,自然得到的tem是小的那一个)
}
}
int main()
{
long long int a, n, b,i=0,o[100000];
while (scanf("%lld %lld %lld", &a, &n, &b) != EOF)
{
o[i]=oy(a,n,b);
i++;
}
for(int j=0;j<i;j++)
{
printf("%d\n",o[j]);
}
return 0;
}
例五:分型三角形
题目描述
有一些少数民族的图腾是分形三角形,你能画出来吗?
输入格式
一个整数 n,表示图腾的大小。
输出格式
若干行,表示图腾的样子。
样例1
输入
3
输出
/\
/__\
/\ /\
/__\/__\
/\ /\
/__\ /__\
/\ /\ /\ /\
/__\/__\/__\/__\
数据规模与约定
1≤n≤10
ps:补充爬楼梯的一道小习题
1.超级楼梯
题目描述
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
输入格式
输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每行包含一个整数M(1<=M<=40),表示楼梯的级数。
输出格式
对于每个测试实例,请输出不同走法的数量
样例
输入
2
2
3
输出
1
2
这题很简单我就不再赘述
//超级楼梯
#include<stdio.h>
int oy(int n)
{
if(n==1)
return 1;
if(n==2)
return 1;
else
return oy(n-1)+oy(n-2);//同理的思维,以3(2和1),4(3和2)得出出口必须有1或2
}
int main()
{
int i,a,b[10000];
scanf("%d",&a);
for(i=0;i<a;i++)
{
scanf("%d",&b[i]);
b[i]=oy(b[i]);
}
for(int j=0;j<i;j++)
{
printf("%d\n",b[j]);
}
return 0;
}
再写下去太长了,期待下次更新在和大家见面!
如果这个能帮助到未来的你,我不胜感激