目录
进阶:二维数组逆递推例题:与隔壁王大爷下象棋(马拦过河卒)
十年饮冰,难凉热血
❤
前言:
递推,一种十分初级的算法,其实我们在一开始学c语言的for循环的时候有可能就接触过这种类型的题,只是当时我们还不知道这种算法,他竟然有名字叫递推,然后发现“就这?就这?”足以发现这个递推的初级算法是多么的初级和简单。好了好了,接下来我们来步入正题,递推是个什么鬼?
正文:
定义:递推,顾名思义就是由简到繁慢慢的一步步的推导出来你想要的结果,百度百科中是这么说的是一种用若干步可重复运算来描述复杂问题的方法。递推是序列计算中的一种常用算法。通常是通过计算前面的一些项来得出序列中的指定项的值。当然,定义了解一下就可以,我认为还是要从例题中慢慢窥探递推的奇妙之处
递推关系是一种简洁高效的常见数学模型。
特点:在递推问题中,每个数据项都和它前面的若干个数据项(或后面的若干个数据项)有一定的关联,这种关联一般通过“递推关系式”表示。而关键就是要找到这个递推关系式
问题求解一般从初始的一个或若干个数据项出发,通过递推关系逐步推进,从而得到最终结果,这种求解问题的方法叫“递推法”。其中,初始的若干数据项称为“边界”。
引入例题1:斐波那契数列
题目为:
斐波那契数列:已知f(1) = 1 , f(2) = 1 , 且满足关系式f(n) = f(n-1) + f(n-2),输出斐波那契数列的前五十项。
解析:其实这个题目的关键点就在于f(n) = f(n-1) + f(n-2)这个神奇的表达式已经知道了,而且知道了f(1) = 1 , f(2) = 1,我们就可以肆无忌惮的用这个表达式和初始值了,然后捏,但是有的题目他是不会告诉你这个表达式的需要你通过你的理解来一步步的通过一到二然后到无穷,所以递推的算法题的解题关键就是找到这个蕴含的表达式和一开始我们知道的默认的初始值就是上文的边界接下来就如鱼得水了。
斐波那契数列的具体代码如下(相信大家都已经会写了):
#include <stdio.h>
int main(void)
{
int long long num[50];//这里一定要用longlong不然长度会不够的!!!
num[0] = num[1] = 1;
printf("%lld\n%lld\n" , num[0] , num[1]);
int i;
for (i = 2 ; i < 50 ; i++)
{
num[i] = num[i - 1] + num[i - 2];
printf("%lld\n" , num[i]);
}
return 0;
}
一维数组递推例题1:母牛的故事
有一头母牛,从第二年起,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?
好,向我们之前在这个斐波那契数列中的出的结论一样,我们要找这个题目的关系式就像“f(n) = f(n-1) + f(n-2)”一样的一个key表达式。
怎么找key表达式呢???
这边建议画个关系图,当然如果你是大佬,一眼就看出来了这个表达式你也可以不画图,但是像我这样的萌新还是画个图来找这个关系式子比较保险,总之建议画图!!然后母牛和小牛的关系就清晰可见了!就像我们小学二年级的找规律题一样简单!!出来这个关系式f[n]=f[n-1]+f[n-3],然后可以发现前四年的数是固定的,而且我们式子中用到了n-3,所以可以不用初始化第四年的值直接初始化前三年的就可,再用一个数组做就十分的简单了!
代码如下:
#include <stdio.h>
int main()
{
int n,i;
scanf("%d",&n);
int f[n];
f[0]=1;f[1]=2;f[2]=3;
for (i=3;i<n;i++)
{
f[i]=f[i-1]+f[i-3];
}
printf("%d",f[n-1]);
return 0;
}
可见,只要找到了递推表达式这些题就和小学二年级的数学题一样简单!!
一维数组逆递推例题2:插方块的故事
题目呢是这样描述的:在2×n的长方形方格中,用n个1×2的骨牌插满方格,输入n ,输出插放方案的总数。
例如 n=3时,为2×3方格,骨牌的插放方案有三种方法,如下图所示:
我们这时候要寻找他的key表达式应该怎么寻找呢?
显而易见,他只能有两种插入方式一种是竖着插,一种是横着插
而且我们插一插就不难发现:当n=1时,只能是一种插法,即f(1)= 1,如下左图所示:
当n=3时可以第一列可以分为两种情况
1.是横插如下图
2.是竖插如下图
这样不难发现 f[3]=f[1]+f[2]了
而n个格子的时候同样的开始铺放时有两种铺放方法一种是横插一种是竖着插:
所以此时如果横着着插就有剩下(n-1)个方块而这(n-1)个方块就有f(n-1)种插法,而如果
是竖着插这(n-2)个剩余的方块就有f(n-2)中插法而f(n-1)和f(n-2)同样的可以这样逆推下去最后返回到了一个我们知道的最初的式子f[3]=f[1]+f[2]
也就是说f(n)=f(n-1)+f(n-2)。神器在手,而神器的柄就是f[1]=1和f[2]=2这两个特殊的量了!
代码如下
#include <stdio.h>
int main()
{
int i,n;
scanf("%d",&n);
long long int f[n];//定义long long完全因为其太大和斐波那契一样
f[0]=1;f[1]=2;
for (i=2;i<n;i++)
{
f[i]=f[i-1]+f[i-2];
}
printf("%lld",f[n-1]);
return 0;
}
都看到这里了奖励你们看会我老婆的照片休息一下吧。
进阶:二维数组逆递推例题:与隔壁王大爷下象棋(马拦过河卒)
棋盘上A点有一个过河卒,需要走到目标B点。象棋中卒行走的规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如下图中的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点(x,y)(如下图中的C点和P1,P2,……,P8,注:马走“日”字。)。卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在从键盘输入n,m,要你计算出卒从A点能够到达B点的路径的条数。
我心想着人家卒还能向左走来咋不让人家向左走?不管那么多了快破王大爷棋阵吧
这个是无解的忽略题目中的红线 :]
我们一看到这个题就应该明白,明白什么呢?这是一个平面!而我们小学二年级之前就学到过二维数组可以表示一个平面。瞬间,很快啊!我们就知道应该用二维数组f[n]来表示卒的行进路线1表示有一条路2表示两条...那么小学一年级的同学又要问了:那可爱的小马和他控制的地方怎么办呢???当然是用另一个二维数组g[m]来表示了,初始化的时候我们单独来初始化就行了!注意:在我们初始化f[n]的第一行第一列的时候不要忘记这条路可能被马封死!就都变成0了!
初始化代码如下:
#include<stdio.h>
int main()
{
int i,j,n,m,f[20][20],g[20][20],x,y;
scanf("%d %d %d %d",&n,&m,&x,&y);
for (i=1;i<=n;i++)//路径全部初始化为0
for (j=1;j<=m;j++)
f[i][j]=0;
for (i=1;i<=n;i++)//控制点全部初始化为0
for (j=1;j<=m;j++)
g[i][j]=0;
//马和控制点设为1
g[x][y]=1;
g[x-1][y-2]=1;
g[x+1][y-2]=1;
g[x-2][y-1]=1;
g[x+2][y-1]=1;
g[x-2][y+1]=1;
g[x+2][y+1]=1;
g[x-1][y+2]=1;
g[x+1][y+2]=1;
//第一列初始化
for (i=1;i<=n;i++)
if(g[i][0]!=1) f[i][0]=1;
else for(;i<=n;i++)
f[i][0]=0;
//第一行初始化
for (j=1;j<=m;j++)
if(g[0][j]!=1) f[0][j]=1;
else for(;j<=m;j++)
f[0][j]=0;
然后呢如果下一个点的上方的数加一个点的左方的数就等于能到达这个点的数了
如果你还不明白可以看一下下面这个动图(自己画的忽略字不好看):
如此这般我们便可以得到这个key表达式:f[i][j]=f[i-1][j]+f[i][j-1]将计算机看不懂的路径化成数字是一个十分好用的方法。这时候又有幼儿园的小朋友会问那么边界怎么办呢?宝,上面都初始化好了
所以总代码如下:
#include <stdio.h>
int max (int a, int b);
int max (int a, int b)
{
if(a>b)return a;
else return b;
}
int main()
{
int n,m,x,y;
scanf("%d%d%d%d",&n,&m,&x,&y);
int a[16][16]={0},dp[16][16]={0};
int i,j;
a[x][y]=1;
a[x-2][y+1]=1;
a[x+2][y+1]=1;
a[x-2][y-1]=1;
a[x+2][y-1]=1;
a[x-1][y+2]=1;
a[x+1][y+2]=1;
a[x-1][y-2]=1;
a[x+1][y-2]=1;
for (i=0;i<=n;i++)
{
if(a[i][0]!=1)
dp[i][0]=1;
else break;
}
for (j=0;j<=m;j++)
{
if(a[0][j]!=1)
dp[0][j]=1;
else break;
}
for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
{
if(a[i][j]!=1)
{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
for (i=0;i<=n;i++)
{
for (j=0;j<=m;j++)
{
printf("%d ",dp[i][j]);
}
printf("\n");
}
}
这样,二维数组的递归问题就解决了,博主也只研究到这了,如果以后还有递归方面的知识博主还会更新的!
不要忘记点赞评论收藏转发关注哦!
十年饮冰,难凉热血
❤