基础算法--递推算法[信奥一本通]

news2024/11/15 8:07:29

本节所讲题源自【信奥一本通】C++版:基础算法-第三章-递推算法


相信大家应该都接触过数列的概念。哎哟,一直在跟数组打交道,说数列感觉好陌生,哈哈。数列中的迭代法大家都还记得吗:通过反复应用特定规则,推导出某一点起始的连续的后续数列。

我们的递推也是这样,给出一些初始值,从题目中找出后续数据应该与已知数据存在哪些关系,能不能写出一个公式或者经过同种操作进行反复推导,得出结论。在数学中我们是数列+递推公式+迭代计算,对应到我们的编码中,就是数组+递推公式+循环实现。

我们前几篇讲的前缀和,高精度计算都有递推的思想在其中,包括后续的递归,搜索,动态规划,回溯等等等大概念算法和小概念算法都需要本节作为基础。 本节就由浅入深练习一些递推的题目。

Part_1:数列部分

1188:菲波那契数列(2)

时间限制: 1000 ms         内存限制: 65536 KB
提交数:80480    通过数: 30981

【题目描述】

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

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

【输入】

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

【输出】

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

本题不难发现以下规律:

a[1]=1,a[2]=1,a[3]=a[2]+a[1]=2,a[4]=a[3]+a[2]=3,a[5]=a[4]+a[3]=5......a[n]+a[n-1]+a[n];

而且本题不同于传统的斐波那契数列编程题,还需要注意以下编程细节:

1.由于越往后计算,数就越大,1000000(1e6)这个数字本身int能够存下,但这是递推啊,int就别用了,尽量用long long类型。但题目告诉我们需要结果对1000取模,这说明好像longlong也存不下,需要进行特殊的处理--取余。

2.多行输入,如果每次我们都进行一次递推,当然可以,但是你想过吗:我先说求斐波那契数列第100项的结果,下面紧跟着就又要第99项,恼不恼火,没办法,人家就这么测试你,你有办法吗,没事,我有,而且我也打算告诉你:求出斐波那契数列的每一项存到数组中,用的时候直接访问即可。这里又要问了:我怎么知道我一开始要求出多少项才够。我只能说:施主,你着相了。那a的范围搁那摆着呢,你求前1e6项就完了呗。你觉得浪费空间,还可能浪费时间,没事,还有办法,我们一开始可以设置一个max,等我们输入完之后再动态开辟max+1的数组空间,我们只求前max项就行了。

ok,重点都分析完了,开始敲代码!!!

#include <bits/stdc++.h>
using namespace std;
#define Long long long

int main()
{
	int n; cin >> n;
	vector<int> v(n);
	int max = 0;
	for (int i = 0; i < n; i++) {
		cin >> v[i];
		if (v[i] > max)max = v[i];
	}
	vector<Long> fib(max + 1);
	fib[1] = 1;
	for (int i = 2; i <= max; i++) {
		fib[i] = (fib[i - 1]%1000 + fib[i - 2]%1000)%1000;
	}
	for (auto e : v) {
		cout << fib[e] << endl;
	}
	return 0;

在编码过程中,你有可能会遇到的问题是:模运算问题。

如果我们相求(a+b)%mod,但是a+b的结果可能会超出范围,再求模取余会影响结果。(当然,本题对1000取模,不影响啥结果,你直接写(a+b)%mod没事)。在初等数论中,我们有一个正规公式:

(a+b)%mod=(a%mod+b%mod)%mod 

首先我们分别对a, b取模, 保证数据小于mod, 然后将数据相加, 再取模, 才能保证结果仍然小于mod

举个例子:(5+2)%3=1,其中5%3=2,2%3=2,相加结果为4,取模4%3=1,即(5%3+2%3)%3。

这时候你该问了,那我直接取余不行嘛。那我们分开求模的目的就是为了解决数据存不下的问题,加入我们数据只能存的下5,连6都存不下,你说5+2存储的时候会是多少,你无法保证这样存储方式下你的数据不会出现丢失等错误,那我们提前对每个数求模取余的必要性就是这样的。

总之一句话:让你求模时,就用这个模运算公式,包不会错。

1189:Pell数列

时间限制: 1000 ms         内存限制: 65536 KB
提交数:53473    通过数: 26840

【题目描述】

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

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

【输入】

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

【输出】

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

本题的规律:

a[1]=1,a[2]=2, ...... a[n]=2*a[n-1]+a[n-2];

需要注意的点:

1.取余,利用模运算公式

2.多行输入,采取数组存储

敲代码!!!

#include <bits/stdc++.h>
using namespace std;
#define Long long long

/*Pell数列*/
int main() {
	int n; cin >> n;
	vector<int> v(n);
	int upper = 0;
	for (int i = 0; i < n; i++) {
		cin >> v[i];
		upper = max(upper, v[i]);
	}
	vector<Long> pell(upper+1);
	pell[1] = 1;
	for (int i = 2; i <= upper;i++) {
		pell[i] = (2*pell[i - 1] % 32767 + pell[i - 2] % 32767) % 32767;
	}

	for (auto e : v) {
		cout << pell[e] << endl;
	}
	return 0;
}

对比一下上面两道题,是不是一个模子里刻出来的两兄弟。递推难吗?不难,只要有公式,那就是一通循环。不难嘛?递推公式题目不给你,你能发现嘛。

Part_2:找规律

1190:上台阶

时间限制: 1000 ms         内存限制: 65536 KB
提交数:83331    通过数: 28970

【题目描述】

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

【输入】

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

【输出】

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

不就是找规律嘛,我们可以用归纳总结的方法,对吧。(哎呀,突然发现数学有点重要了,数学知识+数论学习+数学方法,搞算法嘛,数学这个工具重要的很,不想在算法领域进军的话,有基础就行)

让我们发现规律:

台阶数为n,假设上台阶的走法有为f[n]。

n=1, f[n]=1;(我只能走一步嘛,就这一种方法)

n=2, f[n]=2;(我能一步一步走,走两步,也能走两步一次到位)

n=3, f[n]=4;(一步一步走+1,先走一步再走两步+1,先走两步再走一步+1,一下三步+1)

n=4, f[n]=7;  n=5, f[n]=13;  n=6, f[n]=24......(自己列一下喽)

那么你发现规律了嘛:f[1]=1,f[2]=2,f[3]=4....f[n]=f[n-1]+f[n-2]+f[n-3]

这是根据数据方面发现的规律,但你知道,为什么是这么个规律嘛?

假设当前台阶阶数为m阶,我开局的选择只有3条路:跳1阶,跳2阶,跳3阶。然后呢,假设我选第一条路,剩下还有几阶m-1阶,但是我们一路递推过来到了m阶,m-1阶我们能不知道?这样的话,我们将三条路的情况加起来就是f[m]=f[m-1]+f[m-2]+f[m-3]。此刻的你应该恍然大悟:这代码不用你说,我会了!

#include <bits/stdc++.h>
using namespace std;
#define Long long long


int main() {
	vector<int> v;
	int input;
	while(1){
		cin >> input;
		if (input == 0)break;
		else v.push_back(input);
	}
	vector<Long> ans(71);
	ans[1] = 1; ans[2] = 2; ans[3] = 4;
	for (int i = 4; i < 71; i++) {
		ans[i] = ans[i - 1] + ans[i - 2] + ans[i - 3];
		//第一步跳1个台阶,剩下i-1个台阶取i-1阶台阶的跳法
		//第一步跳2个台阶,剩下i-2个台阶取i-2阶台阶的跳法
		//第一步跳3个台阶,剩下i-3个台阶取i-3阶台阶的跳法
	}
	for (auto e : v) {
		cout << ans[e] << endl;
	}
	return 0;
}

 

1194:移动路线


时间限制: 1000 ms         内存限制: 65536 KB
提交数:25983    通过数: 19703

【题目描述】

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

小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把蚂蚁的右脚弄伤了,于是蚂蚁只能向上或向右移动。小明把这只蚂蚁放在左下角的方格中,蚂蚁从左下角的方格中移动到右上角的方格中,每步移动一个方格。蚂蚁始终在方格矩阵内移动,请计算出不同的移动路线的数目。

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

(2,1)(2,2)(2,3)
(1,1)(1,2)(1,3)

蚂蚁共有3种移动路线:

路线1:(1,1) → (1,2) → (1,3) → (2,3)

路线2:(1,1) → (1,2) → (2,2) → (2,3)

路线3:(1,1) → (2,1) → (2,2) → (2,3)

【输入】

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

【输出】

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

仔细读题,这是个二维问题,但是不妨碍我们找规律啊给大家画个网格模拟移动路线:

按照数学思维,你习惯题目所述的左下角为原点,但是在计算机中,用数组模拟二维时,将坐标系顺时针旋转90°更合适。然后让我们开始找规律:

首先我们知道,蚂蚁的行走规则是,只能沿x-y轴的正方向进行移动,小蚂蚁在1行1列的方格是原地踏步,结果为1。小蚂蚁移动到(1,2)的方式也为1,移动到(2,1)的方式也为1,但是移动到(2,2)的方式为2,因为小蚂蚁可以从上方过来,也可以从左方过来。小蚂蚁移动到(1,3)的方式为1,他只能一步步从左方走过来。移动到(2,3)的方式却为3,同样的道理,它上一步一定在(1,3)或者在(2,2),它到(1,3)只有一种方式,再向下走一步就到了(2,3),它到(2,2)有两种方式,每种方式都再向右走一步,就到了(2,3),到达这个位置又多了两种方式。规律已经呼之欲出了。

我们定义一个二维数组a[m+1][n+1];那么到达某一个方格的方式就是a[i][1]=1,a[1][j]=1;a[x][y]=a[x-1][y]+a[x][y-1]。

#include <bits/stdc++.h>
using namespace std;
#define Long long long

int main() {
	int n, m; cin >> n >> m;
	vector<vector<int>> v(n+1, vector<int>(m+1));
	v[1][1] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
            if(i==1||j==1) v[i][j]=1;
			else v[i][j] = v[i - 1][j] + v[i][j - 1];
		}
	}
	cout << v[n][m];
	return 0;
}

这像不像求前缀和的步骤,前缀和其实就是简单的递推。

让我们换个方式,模拟一下:

一开始我们假设到达所有方格都是0种方式,然后根据题意给(1,1)赋上初值1。开始从a[1][1]遍历,如果a[1][1]右面还有路,到达右面方格的路线就等于原来的方式加上从左面来的这个方格原来的方式。如果a[1][1]下面还有路,到达下面方格的路线就等于原来的方式加上从上面来的这个方格原来的方式。遍历一次,就能将所有的方格的路线数确定,其实这才是从前往后的递推思想 ,从一开始往后推(但是我们的编码中并没有明确的递推公式,所以说这个方式为递推也不合适,但你只要知道这个推导的思想就可以了),前面我们说的都是带点递归的思想,从某一个值往前推,推到已知的值,然后反向得出递推的公式。

#include <bits/stdc++.h>
using namespace std;
#define Long long long

int main() {
	int n, m; cin >> n >> m;
	vector<vector<int>> v(n+1, vector<int>(m+1));
	v[1][1] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
            if(i+1<=n) v[i+1][j]+=v[i][j];
            if(j+1<=m) v[i][j+1]+=v[i][j];
		}
	}
	cout << v[n][m];
	return 0;
}

Part_3:作业(附参考答案)

昆虫繁殖

信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)icon-default.png?t=N7T8http://ybt.ssoier.cn:8088/problem_show.php?pid=1312

#include <bits/stdc++.h>
using namespace std;
#define Long long long

Long ans[52];//第i月成虫数量
Long tmp[52];//第i月卵的数量
/*昆虫繁殖*/
int main() {
	int x, y, z; cin >> x >> y >> z;
	ans[1] = 1; 
	for (int i = 1; i <= x; i++) {
		ans[i] = 1; 
	}
	for (int i = x+1; i <= z+1; i++) {
		ans[i]=ans[i-1]+tmp[i-2];//该月的成虫=上月的成虫+上上个月的卵数
		tmp[i] = ans[i - x] * y;//该月为成虫产卵数=x月前成虫*y
	}
	cout << ans[z+1] << endl;
	return 0;
}

位数问题

信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)icon-default.png?t=N7T8http://ybt.ssoier.cn:8088/problem_show.php?pid=1313

#include <bits/stdc++.h>
using namespace std;
#define Long long long

/*位数问题*/
const int MAX_N = 1001;
Long os[MAX_N];//偶数个数
Long js[MAX_N];//奇数个数
int main() {
	int n; cin >> n;
	os[1] = 8;
	js[1] = 1;
	for (int i = 2; i <= n; i++) {
		os[i] = ((9 * os[i - 1]) % 12345 + (js[i - 1]) % 12345) % 12345;
		js[i] = ((9 * js[i - 1]) % 12345 + (os[i - 1]) % 12345) % 12345;
	}
	os[1] += 1;//把0加上
	cout << os[n];
	return 0;
}

过河卒 

信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)icon-default.png?t=N7T8http://ybt.ssoier.cn:8088/problem_show.php?pid=1314

	
#include <bits/stdc++.h>
using namespace std;
#define Long long long

int main() {
	int n, m; cin >> n >> m;
	vector<vector<Long>> v(n+1, vector<Long>(m+1));
	int cx, cy; cin >> cx >> cy;
	v[0][0] = 1;
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			if (i == 0 || j == 0) {
				if (i == 0 && j > 0) v[i][j] = v[i][j - 1];
				if (j == 0 && i > 0) v[i][j] = v[i - 1][j];
			}
			else v[i][j] = v[i - 1][j] + v[i][j - 1];

			//九大控制点
			if (i == cx && j == cy)v[i][j] = 0;
			if ((i == cx - 1 || i == cx + 1) && (j == cy - 2 || j == cy + 2))v[i][j] = 0;
			if ((i == cx - 2 || i == cx + 2) && (j == cy - 1 || j == cy + 1))v[i][j] = 0;
		}
	}
	cout << v[n][m];
	return 0;
}


感谢大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2074835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统中的Btrfs技术

在Linux操作系统中&#xff0c;文件系统扮演着至关重要的角色&#xff0c;负责管理数据存储、文件访问以及系统的稳定性。其中&#xff0c;Btrfs&#xff08;B-tree file system&#xff09;作为一种先进的文件系统技术&#xff0c;正在逐渐引起广泛关注和应用。本文将深入探讨…

【算法】深入浅出聚类算法:原理、应用与Java实现

一、引言 在数据分析和机器学习中&#xff0c;聚类算法是一种无监督学习技术&#xff0c;用于将数据集中的对象自动划分为多个子集&#xff0c;每个子集称为一个簇。聚类算法在多个领域有着广泛的应用&#xff0c;如图像处理、信息检索、市场细分、生物信息学等。本文将介绍聚…

匿名函数详解

lambda表达式&#xff1a; [](){} []捕获列表 () 函数的参数列表 {}函数的函数体 #include<iostream> #include<string> using std::cout; using std::endl; using std::string; void func() {cout << "hello func" << endl; }void tes…

【系统分析师】-WEB开发技术

目录 1、负载均衡技术 1.1、应用层负载均衡 1.2、传输层负载均衡 2、有状态和无状态问题 3、CDN内容分发网络 4、持久化技术 1、负载均衡技术 1.1、应用层负载均衡 1&#xff09;http 重定向 HTTP 重定向就是应用层的请求转发。用户的请求其实已经到了HTTP重定向负载均…

深度学习--负采样技术及其扩展详解

负采样技术及其扩展详解 负采样&#xff08;Negative Sampling&#xff09;是一种常用于自然语言处理和推荐系统中的技术&#xff0c;主要目的是优化模型的训练效率和效果。负采样技术的典型应用场景包括词向量训练&#xff08;如Word2Vec&#xff09;、推荐系统中的隐语义模型…

使用 Nuxt 的 showError 显示全屏错误页面

title: 使用 Nuxt 的 showError 显示全屏错误页面 date: 2024/8/26 updated: 2024/8/26 author: cmdragon excerpt: 摘要:本文介绍Nuxt.js中的showError方法用于显示全屏错误页面,包括其参数类型及使用方式,并演示了如何在页面中捕获并展示错误,还介绍了useError用于管理…

MySQL笔记-对mysql.sock.lock认识(2024-06-12)

此篇博文记录到个人笔记的时间为2024-06-12。 背景 每次服务器非正常关机&#xff0c;或者制作的docker镜像有问题时或没手动停mysql时&#xff0c;运行 mysqld --userroot 时&#xff0c;mysql总会启动失败&#xff0c;查看/var/log/mysqld.log时会有如下报错信息&#xff…

你的软件系统安全吗

如果你的软件系统可以通过网络访问, 可以由多人操作, 可以访问或操作敏感数据, 或者可能暴露隐私, 等等, 请一定要保护你的系统. 那么你的软件系统安全吗? 一. 先问自己如下 4 个问题 1. What are we working on? 我们的系统在干啥? 例如电商系统: 我们在卖啥?提供什么售前…

【变化检测】基于Tinycd建筑物(LEVIR-CD)变化检测实战及ONNX推理

主要内容如下&#xff1a; 1、LEVIR-CD数据集介绍及下载 2、运行环境安装 3、Tinycd模型训练与预测 4、Onnx运行及可视化 运行环境&#xff1a;Python3.8&#xff0c;torch1.12.0cu113 likyoo变化检测源码&#xff1a;https://github.com/likyoo/open-cd 使用情况&#xff1a…

在vue2中,使用计算属性,具体代码如下:

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

爆改YOLOv8 | 利用CPA-Enhancer提高低照度物体检测(适用于雨,雪,雾天)

1&#xff0c;本文介绍 CPA-Enhancer通过链式思考提示机制实现了对未知退化条件下图像的自适应增强&#xff0c;显著提升了物体检测性能。其插件式设计便于集成到现有检测框架中&#xff0c;并在物体检测及其他视觉任务中设立了新的性能标准&#xff0c;展现了广泛的应用潜力。…

打包资料优化目录

这篇文章主要写一下这一次更新的几个地方&#xff0c;有对原来的代码及模型进行优化的部分&#xff0c;也有新增加的代码和模型&#xff0c;我就把几个比较典型的给列了出来。但是还有好多的更新没有在下面展示出来&#xff0c;因为一个个展示出来太复杂了。如果你对更新的内容…

mybatis框架搭建、mybatis打印日志设置、参数传递使用、myatis插件MyBatisX

一、框架 就是对技术的封装&#xff0c;将基础的技术进行封装&#xff0c;让程序员可以快速的使用&#xff0c;提高效率。 Java后端框架&#xff1a; mybatis&#xff1a;对jdbc进行封装 spring&#xff1a;对整个Java后端架构进行管理的 springweb&#xff1a;对web层&a…

用Python解决优化问题_整数规划模板

整数规划的基本概念 整数规划是一种数学优化方法&#xff0c;它是线性规划的一个扩展。在整数规划中&#xff0c;决策变量被限制为整数&#xff0c;而不是连续的值。这种类型的规划在许多实际应用中非常重要&#xff0c;例如资源分配、生产计划、物流配送等。整数规划可以分为…

R7RS标准之重要特性及用法实例(三十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

【数据库】深入浅出MySQL SQL优化:原因、定位、分析与索引失效

这是一张AI生成关于MySQL SQL优化的插图。图中展示了一个计算机屏幕&#xff0c;上面可以看到MySQL数据库模式。屏幕周围有代表优化的视觉隐喻&#xff0c;如齿轮、闪电和流线型形状。屏幕上的模式用色彩丰富的注释标出了改进区域&#xff0c;如索引和查询调整。整体风格现代且…

【源码+文档+调试讲解】数据结构课程网络学习平台

摘要 本文介绍了数据结构课程网络学习平台的开发全过程。通过分析企业对于数据结构课程网络学习平台的需求&#xff0c;创建了一个计算机管理数据结构课程网络学习平台的方案。文章介绍了数据结构课程网络学习平台的系统分析部分&#xff0c;包括可行性分析等&#xff0c;系统设…

Python处理JSON

Python处理JSON ####概念 序列化&#xff08;Serialization&#xff09;&#xff1a;将对象的状态信息转换为可以存储或可以通过网络传输的过程&#xff0c;传输的格式可以是JSON、XML等。反序列化就是从存储区域&#xff08;JSON&#xff0c;XML&#xff09;读取反序列化对象…

优化学习管理:Moodle和ONLYOFFICE文档编辑器的完美结合

目录 前言 一、什么是 Moodle 1、简单快速插入表单字段 3、免费表单模板库 4、开启无缝协作 三、在Moodle中集成ONLYOFFICE文档 四、在Moodle安装使用ONLYOFFICE 1、下载安装 2、配置服务器 3、在Moodle中使用ONLYOFFICE 文档活动 五、未来展望 写在最后 前言 在当今教育科技飞…

前端如何在30秒内实现吸管拾色器?

⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享 前端react——实现浏览器页面的吸管拾色器功能。 背景&#xff1a; 在chrome web端快速实现一个页面的取色器功能&#xff0c; 分为两个场景 固定区域小范围取色当前页面取色 node系列往期文章 node_windows环境变量…