蓝桥杯备赛——算法初阶入门

目录

内容简介

1.模拟

2.高精度

3.枚举

3.1 普通枚举

3.2二进制枚举

4.前缀和


内容简介

备赛蓝桥杯c++组期间,大致总结初阶的一些基础入门算法。在每个篇章里面我会写一些重点题目和做题技巧。

1.模拟

模拟题目算是蓝桥杯里的签到题,一般题目会给出具体的操作,我们只需要按照题目给的内容模拟出操作即可。容易出错的点在于代码实现中的情况判断,在复杂的模拟题中,我们很容易遗漏某种情况导致出错。

P5731 【深基5.习6】蛇形方阵 - 洛谷

基于此类的题目,一般会给出一个矩阵,让我们按题目规律在矩阵中填数。针对此类题目,我们可以使用通解。

1.定义四个方向变量,对应上下左右方向。

2.根据题目要求,通过加上方向变量的值,控制下一个填数方向。

3.在一个方向填数的时候,走到最后会出现越界的情况,当判断走到越界的时候,我们需要更新方向变量,控制下一个新的填数方向。

#include <iostream>

using namespace std;

const int N=10;
int n;
int arr[N][N];
//方向变量:右下左上 
int dx[4]={0,1,0,-1}; 
int dy[4]={1,0,-1,0};
 

int main(){
	cin>>n;
	int x=1,y=1;//记录当前填入位置 
	int cnt=1;//记录当前填入的数
	int pos=0;//记录当前方向 
	while(cnt<=n*n){
		//在当前位置填数
		arr[x][y]=cnt;
		//计算下一个位置坐标
		int a=x+dx[pos],b=y+dy[pos];
		//判断下一个位置是否合法
		if(b>n||a>n||b<1||arr[a][b]!=0)//越界
		{
			pos=(pos+1)%4;//更新方向 
			a=x+dx[pos],b=y+dy[pos];//计算正确位置 
		}
		x=a,y=b;//更新位置 
		cnt++;//更新填数 
	} 
	//打印
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			printf("%3d",arr[i][j]);
		}
		cout<<endl;
	} 
}

模拟题的重点就是细化情况,一定需要条理逻辑清晰,考虑细节,分类讨论。

2.高精度

在蓝桥杯比赛中,几乎每题都会给出数据的范围,有部分题目给出的数据范围超过了C++数据的最大范围,所有的类型都不够存储数据,此时我们需要用到高精度算法来计算,本质也是模拟算法。

1.用字符串读入数据,再把数据的每一位逆序存放到数组

2.利用数组,进行模拟运算

我们用高进度算法进行模拟加减乘除运算的时候,基本的注意点就是:逆序存放,求当前位的计算结果,处理余数,处理进位,处理结果前导零。

P1601 A+B Problem(高精) - 洛谷

P2142 高精度减法 - 洛谷

P1303 A*B Problem - 洛谷

P1480 A/B Problem - 洛谷

高精度减法中,我们需要注意让绝对值较大的数字减去绝对值较小的数字,因为我们使用字符串读入数字,默认采用字典序的排序方式来判断大小。例如:99和100,程序会认为99>100,因为第一位9>1。

所以我们在字典序判断之前可以比较两个字符串的长度,字符串长度较长的数会较大。如果字符串长度相等,我们再采用字典序判断,这样就不会出现误判。

高进度乘法中,我们在计算当前位之后不用着急处理进位,而是先放置不动,继续计算下一位的值。等所有对应位置都乘完,并且累加完毕。我们再去统一处理进位。

3.枚举

枚举,顾名思义就是把所有的情况都列举出来,再根据题目判断是否符合要求,是一种暴力求解的方法。所以,在着手写代码之前,我们需要大致判断一下时间复杂度,如果超时,我们则需要采用其他更优的算法。但是枚举也分三六九等,好的枚举可以大幅减少枚举次数,减少超时的情况。

3.1 普通枚举

P1003 [NOIP 2011 提高组] 铺地毯 - 洛谷

题目要求我们求出指定坐标最上面的毯子编号。我们可以运用枚举,列出所有毯子,判断是否可以覆盖到(x,y)坐标,最后覆盖到(x,y)坐标的毯子编号就是答案。

优化:这题的枚举虽然不会超时,但是我们需要从第一张毛毯枚举到最后一张,才可以得到最上面的毛毯编号,有没有什么办法可以优化呢?

        当然是可以的,我们可以从从后往前枚举,这样我们就可以先得到最上面的毛毯编号。当我们枚举的时候,判断当前毛毯是否覆盖(x,y),如果覆盖就直接输出编号即可,不需要全部枚举。

P2010 [NOIP 2016 普及组] 回文日期 - 洛谷

更典型的可以展现枚举优化的题目

1.我们可以枚举年份,根据回文特性推出月日,再判断是否合法。

2.我们可以枚举日月,根据回文特性推出年份,再判断是否合法。

那此时我们选择策略1还是策略2,我们可以简单算一下时间复杂度,策略1的时间复杂度是On,n最大是9999,策略2的时间复杂度是On^{2},具体为月份12×天数30=360。此时,策略2是更优的解法

3.2二进制枚举

二进制枚举中,我们需要掌握位运算的知识,之后才可以更好的运用并理解。

我举一个很简单的例子,商店有四种价格不一样水果,我去商店买水果每种水果最多买一个,也可以不买水果,不限制种类,我有多少种选法,怎么枚举出不同的选法。

每种水果都有两种状态,选法应该是2^{4}种,此时我们就可以用到二进制枚举出不同的选法。

#include <iostream>

using namespace std;

string fruit[4]={"苹果","香蕉","橙子","桃子"}; 
int ret;
int main()
{
	for(int st=0;st<(1<<4);st++){
		ret++;
		if(st==0) {
			cout<<"不选"<<endl; 
			continue;
		}
		for(int i=0;i<4;i++){
			if((st>>i)&1) cout<<fruit[i]<<" ";
		}
		cout<<endl;
	} 
	cout<<"选法总数为"<<ret<<endl; 
	return 0;
} 

第一个for循环,列举了0000,0001,0010,0011……1110,1111等16个数字,第二层for循环,通过(st>>i)&1,就可以得到当前位是否需要选中。例如,st=1001。(st>>0)&1得到1,说明选中苹果;(st>>1)&1得到0,说明不选香蕉;(st>>2)&1得到0,说明不选橙子;(st>>3)&1得到1,说明选中桃子。

78. 子集 - 力扣(LeetCode)

这是很简单的一道模板题,与选水果的方法是一样的,可以用来练手,加强对位运算的理解。

P10449 费解的开关 - 洛谷

当然如果掌握的不错了,可以来试一下这题。难度会更大一点,需要考虑的细节会更多。

4.前缀和

前缀和就是求“前i位”的和,例如arr[5]={1,2,3,4,5}; 前缀和数组f[5]={1,3,6,10,15};相信这个例子是很好理解的,利用代码实现就是循环实现f[i]=f[i]+f[i-1];那我们为什么要建立前缀和数组呢?其实是为了快速解决求[l,r]区间和的问题,我们通过一道题更好的去理解一下。

P1115 最大子段和 - 洛谷

在学习前缀和之前,我们大概率会采用枚举的方法,把所有字段和都枚举出来,找到值最大的字段和。很明显我们需要两层循环来遍历,在本题是会超时的,此时采用前缀和,我们就可以在求[left,right]字段和的时候,通过f[right]-f[left-1]得到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值