简易递推算法与记忆化

摘要:
递归算法!(感谢@SXY大哥教我真正的递归算法,并用魔法打败魔法。感谢上帝本!!!)递归是一种常用的简单算法。递归是一种通过几个可以重复的简单操作来描述复杂问题的方法。递归的特点是每个项都与前面的几个项相关联。这种关联一般可以用递归关系来表示,某个项目的数据可以从前面的几个项目中获得。递归问题的解决通常从一个或多个初始数据项开始,

递推算法!(鸣谢@SXY大佬教我真正的递推算法,用魔法打败魔法,谢谢神犇!!!)

递推
递推是经常被使用的一种简单的算法。递推是一种用若干步可重复的简单运算来描述复杂问题的方法。

递推的特点在于,每一项都和他前面的若干项由一定的关联,这种关联一般可以通过递推关系式来表示,可以通过其前面若干项得出某项的数据。

对于递推问题的求解一般从初始的一个或若干个数据项出发,通过递推关系式逐步推进,从而得出想要的结果,这种求解问题的方法叫递推法。其中,初始的若干数据项称为边界。
————————————————
原文链接:https://blog.csdn.net/Richard__Ting/article/details/79679648

上文是对递推的基本定义是扒的CSDN大佬的文章,附上原文链接,致敬神犇!!!

递推是一种经常用到的简单算法,但是我这种蒟蒻上课的时候莫有听懂!!

但是还要刷题打卡,这可咋整?

当然是走歪门邪道了!!

ybt 1189

【题目描述】

Pell数列a1,a2,a3,...的定义是这样的,a1=1,a2=2,...,an=2*an1+an2(n>2)

给出一个正整数k,要求Pell数列的第k项模上32767是多少。

【输入】

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数k (1≤k<1000000)。

【输出】

n行,每行输出对应一个输入。输出应是一个非负整数。

这是我刷的第一个递推题,题目要求求出PELL数列的第K项对32767的模。

给出的K范围非常大。K的值在一百万以内,不仅爆时间,数据范围也会爆。

我又不会递推……

尝试一下歪门邪道……

上本蒟蒻代码(小声BB)

//1202
#include<iostream>
using namespace std;
int jiyihua[1000000];
int a;
int Pell(int x)
{
    if (jiyihua[x]!=0) 
    {
        return jiyihua[x];
    }
    if (x==1)
    {
        return 1;//递归边界
    }
    if (x==2)
    {
        return 2;
    }
    return jiyihua[x]=(2*Pell(x-1)%32767+Pell(x-2)%32767)%32767;
}
int main()
{
    int n;
    cin>>n;
    for (int i=0;i<n;i++)
    {
        cin>>a;
        cout<<Pell(a)<<endl;
    }
    return 0;
}

记忆化是歪门邪道,下面才是正规做法。

emmmm……反正先AC了再说(逃

上递推代码

//1189递推
#include<iostream>
using namespace std;
int Pell[1000000];
int main()
{
    int n,num;
    cin>>n;
    Pell[1]=1;
    Pell[2]=2;//初始化递推数组
    for (int i=0;i<n;i++)
    {
        cin>>num;
        for (int j=3;j<=num;j++)
        {
            Pell[j]=(Pell[j-1]*2%32767+Pell[j-2]%32767)%32767; //递推式加每步取模,不爆int
        }
        cout<<Pell[num]<<endl;
    }
    return 0;
} 

这是正规军。。。

好了抬走下一位!!!

ybt1188

嗯!又是大家喜闻乐见的斐波那契时间!!!

先上题目!

【题目描述】

菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。

给出一个正整数a,要求菲波那契数列中第a个数对1000取模的结果是多少。

【输入】

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a(1 ≤ a ≤ 1000000)。

【输出】

n行,每行输出对应一个输入。输出应是一个正整数,为菲波那契数列中第a个数对1000取模得到的结果。

需要注意的是,这次斐波那契不是单纯的递归了,出题人把他重新包装了一遍,难度提升了,简直是诚意满满(丧心病狂)!

可以看到,题目中给的a的值也是一百万。这样的话递归也是会爆的(如果我没记错的话递归斐波那契45次就要220000ms以上的时间了,肯定超时)

那么还是选择愉快的递推,递推式很好想,就是题目中给出的斐波那契公式,即 f(n)=f(n-1)+f(n-2)   (n>=3)

递推式有了,程序就好写了(小声BB)

上本蒟蒻代码

//1188
#include<iostream>
using namespace std;
int fei[1000001];//大同小异,先定义递推数组,大小等于n+1
int main()
{
    int n;
    cin>>n;
    fei[1]=1;//初始化递推数组
    fei[2]=1;
    for (int i=0;i<n;i++)
    {
        int num;
        cin>>num;for (int j=3;j<=num;j++)
        {
             fei[j]=(fei[j-1]+fei[j-2])%1000;//开始递推,别忘了取模!!!
        }
        cout<<fei[num]<<endl;//输出答案
    }
    return 0;
}

行了这两个题其实都挺水的,我不BB大家也都能一次就AC。但是我要写递推的博客,所以先水一下博文长度。

我个人才疏学浅,是个不折不扣的蒟蒻,递推的方法我也就用到了这两个题里而已。。。

下面是用物理硬核打败魔法时间。。(正文部分)

ybt1193

先上题目!

【题目描述】

名名的妈妈从外地出差回来,带了一盒好吃又精美的巧克力给名名(盒内共有 N 块巧克力,0<N<20)。妈妈告诉名名每天可以吃一块或者两块巧克力。假设名名每天都吃巧克力,问名名共有多少种不同的吃完巧克力的方案。例如:如果N=1,则名名第1天就吃掉它,共有1种方案;如果N=2,则名名可以第1天吃1块,第2天吃1块,也可以第1天吃2块,共有2种方案;如果N=3,则名名第1天可以吃1块,剩2块,也可以第1天吃2块剩1块,所以名名共有2+1=3种方案;如果N=4,则名名可以第1天吃1块,剩3块,也可以第1天吃2块,剩2块,共有3+2=5种方案。现在给定N,请你写程序求出名名吃巧克力的方案数目。

【输入】

输入只有1行,即整数N。

【输出】

输出只有1行,即名名吃巧克力的方案数。

emmm,这个题,明眼人都看出来是一个斐波那契数列。。。

但是我个人菜鸡,莫有看出来,所以。。

我选择了爆搜。。。先上代码。

//1193
#include<iostream>
using namespace std;
int way; void dfs(int x,int y) { if (x>y)//不能吃的糖比总数还多,不然搜索不能结束,会GG { return; } if (x==y) { way++;//如果搜到一种情况,吃的糖和买的糖一样多,视为一种方法,答案++ return; } dfs(x+1,y);//爆搜时间到!!! dfs(x+2,y); } int main() { int n; cin>>n; dfs(0,n);//一开始没吃糖,所以是0 cout<<way; return 0; }

emmm,出题人比较良心,没有卡我的爆搜。

递推正规方法是把上面的斐波那契程序改成输出n+1项就好了,不多做BB。

抬走下一位。。。

ybt1190

【题目描述】

楼梯有n(71>n>0)阶台阶,上楼时可以一步上1阶,也可以一步上2阶,也可以一步上3阶,编程计算共有多少种不同的走法。

【输入】

输入的每一行包括一组测试数据,即为台阶数n。最后一行为0,表示测试结束。

【输出】

每一行输出对应一行输入的结果,即为走法的数目。

先说递推/归策略!

我们假设要上到第n级台阶。

那么我们可以从第n-3级上3级到第n级,

或者从第n-2级上2级到第n级,

再或者从第n-1级上1级到第n级。

没有别的情况了。。。

所以我们上到第n级台阶的方法有3种,一种是上到n-3级,一种是上到n-2级,还有一种是上到n-1级。

而n-3,n-2,n-1的方法数也是由此可以得出的。

所以,递推/归式就很明显了。

f(n)=f(n-1)+f(n-2)+f(n-3)   (n>3)

我个人这两种方法都没用。。。(因为当时没想这么多,直接搜索的)

上搜索代码。一开始我是用的爆搜,但是出题人学坏了,爆搜被TLE卡死。所以改了一下,改成了记忆化,顺利AC。

//1190
#include<bits/stdc++.h>
using namespace std;
long long mapp[200];//开记忆化数组
long long f(long long step)
{
    if (step==1)
    {
        return 1;//设置递归边界
    }
    if (step==2)
    {
        return 2;
    }
    if (step==3)
    {
        return 4;
    }
    if (mapp[step-2]!=0&&mapp[step-3]!=0&&mapp[step-1]!=0)
    {
        return mapp[step-2]+mapp[step-3]+mapp[step-1];//如果已经被搜过了,就调用记忆化的结果
    }
    return mapp[step]=f(step-1)+f(step-2)+f(step-3);//如果没有被搜过,记忆结果
}
int main()
{
    long long n;//因为是到71,所以结果可能非常大,要用long long才能存,一开始我用的int 各种被爆掉,错了好几遍才幡然醒悟
    for (;;)//用死循环for输入,直到输入零
    {
        cin>>n;
        if (n==0)
        {
            break;
        }
        cout<<f(n)<<endl; //输出答案
    }
    return 0;
} 

下面是正规递推代码

//1190递推
#include<iostream>
using namespace std;
long long ans[72];
int main()
{
    ans[1]=1;
    ans[2]=2;
    ans[3]=4;
    for (;;)
    {
        int n;
        cin>>n;
        if (n==0)
        {
            break;
        }
        for(int j=4;j<=n;j++)
        {
            ans[j]=ans[j-1]+ans[j-2]+ans[j-3];
        }
        cout<<ans[n]<<endl;
    }
    return 0;
}

自己感觉比记忆化好写太多也好想太多,优秀!!!!

ybt1194

这是今天分享的最后一个题啦!!!

先上题目。

【题目描述】

X桌子上有一个m行n列的方格矩阵,将每个方格用坐标表示,行坐标从下到上依次递增,列坐标从左至右依次递增,左下角方格的坐标为(1,1),则右上角方格的坐标为(m,n)。

小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把蚂蚁的右脚弄伤了,于是蚂蚁只能向上或向右移动。小明把这只蚂蚁放在左下角的方格中,蚂蚁从

左下角的方格中移动到右上角的方格中,每步移动一个方格。蚂蚁始终在方格矩阵内移动,请计算出不同的移动路线的数目。

对于1行1列的方格矩阵,蚂蚁原地移动,移动路线数为1;对于1行2列(或2行1列)的方格矩阵,蚂蚁只需一次向右(或向上)移动,移动路线数也为1……

【输入】

输入只有一行,包括两个整数m和n(0 < m+n ≤ 20),代表方格矩阵的行数和列数,m、n之间用空格隔开。

【输出】

输出只有一行,为不同的移动路线的数目。

我们先来看,蚂蚁只能向右或向上移动。

所以……啊啊啊啊我不会递推啊!

还是乖乖搜索。

我们知道mn的数值,可以先初始化一张小地图,然后在上面搜索,枚举路径,再输出答案。

此时mn数值不大,所以用搜索是没有毛病的!!

上代码!!

//1194
#include<iostream>
using namespace std;
int mapp[100][100];//初始化小地图(管他多大,不越界就行)
int m,n,way;
void dfs(int x,int y,int ex,int ey)//x,y是当前坐标,ex,ey是终点坐标
{if (x==ex&&y==ey)//如果蚂蚁走到了终点,那路径+1
    {
        way++;
        return;
    }
    if (mapp[x+1][y]!=0)
    {
        dfs(x+1,y,ex,ey);//向右走
    }
    if (mapp[x][y+1]!=0)
    {
        dfs(x,y+1,ex,ey);//向上走
    }
}
int main()
{
    cin>>m>>n;
    for (int i=m;i>0;i--)//题目要求,地图要像建坐标系一样建立
    {
        for (int j=1;j<=n;j++)
        {
            mapp[i][j]=1;//初始化小地图
        }
    }
    dfs(1,1,m,n);//从(1,1)开始走
    cout<<way;//输出答案
    return 0;
}

那么今天就先写这么多,刷题真的好累啊QAQ!!!

免责声明:文章转载自《简易递推算法与记忆化》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇React 高级指引NOIP 2013 T2 火柴排队 ----&amp;gt;求逆序对下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

ES系列二、Mac 通过docker搭建ELK日志收集系统

#检查是否安装了elkdockerimages#清理以前版本的dockerrmi$#安装elk 6.8.0版本的docker pullslasticsearch:6.8.0 dockerpullskibana:68.0dockerpullogstash:68.00#检查dockerimages2是否查看拉取的ElasticSearch:操作命令:docker...

zookeeper 日志输出到指定文件夹

最近,我在学习ZookeperStormKafka。顺便说一下,我在本地建立了一个集群。我遇到了Zookeeper日志输出路径的问题。我发现设置log4j。Zookeeper中的属性无法解决日志路径问题。我发现解决方案如下:1.修改log4j属性,您应该能够更改它。我更改了红色粗体,但仍然没有生效。#定义要移动的默认值...

VSCode, 当今最流行的免费开源代码编辑器,微软出品,必属精品

Visual Studio代码是一个轻量级但功能强大的源代码编辑器,可以在桌面上运行,可以用于Windows、MacOS和Linux。直接在编辑器中检查差异,暂时保存文件并提交。Visual Studio代码产品在初始操作中的内部代码控制可以通过编辑器内的SCM支持(包括丰富的Git集成)加快发布周期。用户界面-介绍VSCode编辑器的基本UI、命令和功能。...

10 TCP限流技术

TCP流限制的原因是接收方可以完全接受消息,以确保数据安全而不会丢失。首先,窗口机制引入了发送方和接收方都有一个窗口。当发送方发送数据时,将发送落入窗口中的数据。当接收器接收到数据时,落入接收器窗口的数据将被接受。可以看出,流量会受到窗口大小II的限制。滑动窗口技术1TCP滑动窗口技术通过动态改变窗口大小来调整两台主机之间的数据传输。...

MongoDB 查看集合的统计信息

--1查看集合的统计信息srs0:“size”:“ok”:可以理解为集合名称计数:集合中的文档总数大小:连续分配的数据块索引:最近分配的块的大小paddingFactor:所有索引索引的总大小大小:--2显示rs0:db。东西。stats(1024)(KB);{“ns”:“count”:“size”:“indexSize”:...

Python-正则

,三:量词*重复0次或多次{0,}+重复一次或多次{1,}?重复0或1次{1,0}{n}重复n次{n}{n,}重复n次,或更多次{n,m}将n次重复到m次Escape:如果字符串中有特殊字符要匹配,请在常规字符和字符串前面添加r。如果特殊字符在字符组中,则它们是匹配的特殊字符,但为了记忆,匹配时会转义所有特殊字符。...