当前位置:首页 » 《随便一记》 » 正文

【第十四届蓝桥杯考前速成】必考知识点及代码模板总结,看完至少多拿50分

17 人参与  2023年05月07日 08:57  分类 : 《随便一记》  评论

点击全文阅读


文章目录

写在前面一、年份日期问题1、闰年判定2、月份天数 二、简单算法1、前缀和2、差分3、二分4、并查集 二、简单数论1、质数判定2、筛质数3、进制转换(1)其他进制转十进制(2)十进制转其他进制 4、保留小数5、最大公约数6、最小公倍数7、快速幂 三、常用STL1、string2、vector3、queue/priority_queue4、stack5、set/multiset6、map/multimap7、unordered_set/unordered_map8、pair<int,int>9、algorithm 四、简单图论1、单源最短路径2、多源最短路3、最小生成树 五、动态规划1、0-1背包2、完全背包3、多重背包4、线性DP 总结

写在前面

本文章面向第十四届蓝桥杯考生,在最后考前几天,方便自己总结与回顾,同时将其分享给大家,希望我们都能取得心仪的成绩。本文章主要给出代码模版,对算法具体流程不会具体讲解,所以不太适合零基础的同学,适用于有一定算法基础,在考前想要进行复习与总结的同学。文章大部分代码模版参考于y总,同时自己添加了一些常考知识点,y总代码模版参考处。由于时间紧迫,给出了部分选看内容,如果学有余力的同学可以了解一下,在考试中可以多过一些测试点,多拿一些分。

一、年份日期问题

1、闰年判定

闰年:年份能够被4整除但不能被100整除或者能被400整除的为闰年。

代码模版

bool isryear(int n){     return n%400==0||(n%4==0&&n%100!=0);}

2、月份天数

口诀:一三五七八十腊,三十一天永不差。闰年2月29天,平年2月28天。

代码模板

//也可以将数组第一个位置空出来,即填上一个随意的值,这样就可以将月份和数组下标对应了,方便访问int pmonths[]={31,28,31,30,31,30,31,31,30,31,30,31};   //平年每月天数int rmonths[]={31,29,31,30,31,30,31,31,30,31,30,31};   //闰年每年天数

二、简单算法

1、前缀和

一维前缀和

预处理出s[],s[i]存储前i个数的和s[i]=a[1]+a[2]+...+a[i]计算[l,r]区间和=s[r]-s[l-1]

二维前缀和

左上角坐标为(1,1),右下角坐标为(i,j)的前缀和(区域内所有数的和)s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]求左上角坐标为(x1,y1),右下角坐标为(x2,y2)的前缀和s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]
参考例题:这里

2、差分

一维差分

int b[N];       //b为差分数组,直接定义为全局即可,如果要对某个数组进行差分操作,直接先将该数组中每个数进行insert(i,i,a[i])操作即可得到a的差分数组b//对区间[l,r]进行差分操作时void insert(int l,int r,int c){b[l]+=c;b[r+1]-=c;}//差分完后对b数组求前缀和即可,求完前缀和后的b数组即进行完对某些区间加减某个数操作后的原数组

二维差分

int b[N][N];     //差分数组 //对左上角坐标为(l1,r1),右下角坐标为(l2,r2)的矩阵进行差分操作时void insert(int l1,int r1,int l2,int r2,int c){b[l1][r1]+=c;b[l1][r2+1]-=c;b[l2+1][r1]-=c;b[l2+1][r2+1]+=c; } //同样,差分操作完成后对b数组求前缀和,即可得到对原数组某些区域加减某个数后操作的原数组
参考例题:这里

3、二分

//首先确定区间的二段性:二部分分别满足不同的性质。以任意一部分的性质作为check条件,如果mid满足check判断区间应该缩小到哪部分,如果在[l,mid]利用模板1,如果在[mid,r]利用模板2 //模版1int l=0,r=n;    //二分区间[l,r] while(l<r){int mid=l+r>>1;if(check(mid)) r=mid;else l=mid+1;} //模版2int l=0,r=n;    //二分区间[l,r] while(l<r){int mid=l+r+1>>1;if(check(mid)) l=mid;else r=mid-1;} //算法结束l=r,l、r均为结果 
参考例题:这里

4、并查集

代码模板

int p[N];     //祖宗结点数组 for(int i=1;i<=n;i++) p[i]=i;   //初始化 int find(int x){     //查找祖宗结点 if(p[x]!=x) p[x]=find(p[x]);return p[x];} p[find(a)]=find(b);    //合并a,b集合 
参考例题:这里

二、简单数论

1、质数判定

试除法判定质数:时间复杂度O(n2)

代码模板

bool isprimes(int n){if(n<2) return false;for(int i=2;i<=n/i;i++){if(n%i==0) return false;}return true;}

2、筛质数

埃氏筛法:时间复杂度O(nloglogn)

代码模板

bool st[N];    //存储每个数是否被筛掉 int primes[N],cnt;   //primes[]存储每个质数,cnt记录质数的数量 void getprimes(int n){st[0]=st[1]=true;for(int i=2;i<=n;i++){if(!st[i]){primes[cnt++]=i;for(int j=i+i;j<=n;j+=i){st[j]=true;}}}}
线性筛法:参考我的该篇博客参考例题:这里

3、进制转换

(1)其他进制转十进制

利用秦九韶算法O(n)时间计算n进制数转十进制的结果(n小于10,n大于10时特殊判断A~Z字母即可)
//传入为string类型int ntoten(string s,int n){   //n表示传入的是多少进制数,s为n进制数 int ans=0;for(int i=0;i<s.size();i++){ans=ans*n+s[i]-'0';}return ans;}//传入为数组,num为数组中元素个数int ntoten(int a[],int num,int n){    int ans=0;    for(int i=0;i<num;i++){        ans=ans*n+a[i];    }}
刚刚比完的AcWing第97场周赛正好考到了,可以关注我明天发的周赛题解,我发布之后也会在此补上链接(补在下面了)。参考例题:这里

(2)十进制转其他进制

短除法:先得到的余数为低位,即使用短除法直到商为0,余数从下往上排列即为转换后的进制的数。十进制转n进制(n小于10,n小于10,n大于10时需传入字符串思路大同小异,注意特殊处理字母A~Z即可)
//利用栈void tenton(int nums,int n){   //nums为需要转换的数,n为需要转换的进制数    stack<int> s;    while(nums){    s.push(nums%n);    nums/=n;}while(!s.empty()){    cout<<s.top();s.pop();}}//用数组存储,然后反转数组int a[N];void tenton(int nums,int n){   //nums为需要转换的数,n为需要转换的进制数    int cnt=0;     //记录数组中元素个数    while(nums){    a[cnt++]=nums%n;    nums/=n;}reverse(a,a+cnt);for(int i=0;i<cnt;i++) cout<<a[i];}

4、保留小数

头文件#include <iomanip>,利用fixedsetprecision()来实现四舍五入保留任意位数小数。

代码模板

//例:对a保留两位小数cout<<fixed<<setprecision(2)<<a;

5、最大公约数

欧几里得算法:ab的最大公约数等于ba%b的最大公约数。

代码模版

int gcd(int a,int b){return b?gcd(b,a%b):a;}
参考例题:这里

6、最小公倍数

ab的最大公约数与最小公倍数的乘积=a * b,所以最小公倍数=a*b/gcd(a,b)

7、快速幂

代码模板

typedef long long LL;      //需要快速幂的值一般较大,所以开long long//返回a^b%p的结果 int qmi(int a,int b,int p){LL res=1%p;while(b){if(b&1) res=res*a%p;b>>=1;a=(LL)a*a%p;}return res;} 
参考例题:这里

三、常用STL

1、string

#include <string>    //头文件size()      //返回大小empty()     //判断是否为空clear()     //清空substr(起始下标,子串长度)   //返回指定长度子串find()      //查找字符第一次出现的位置,如果没有出现过则返回string::npos//非成员函数stoi()    //将字符串转化成int类型,传入string类型字符串atoi()    //将字符串转化为int类型,传入char类型字符串

2、vector

#include <vector>   //头文件size()        //返回元素个数empty()       //判空clear()      //清空push_back()   //在尾部添加一个元素pop_back()    //删除最后一个元素begin()/end()   //首迭代、尾迭代front()/back()  //返回第一个/最后一个元素

3、queue/priority_queue

注:还有deque,由于本人不怎么使用没有总结,感兴趣的朋友有时间了解一下。

#include <queue>   //头文件//queuesize()    //返回队列中元素的个数empty()   //判空push()    //在末尾加入一个元素pop()     //删除第一个元素front()/back()   //返回第一个/最后一个元素//priority_queuesize()    //返回优先队列中元素的个数empty()   //判空push()    //加入一个元素pop()     //删除堆顶元素top()     //返回堆顶元素默认定义为大根堆//定义成小根堆方法:priority_queue<int,vector<int>,greater<int>> q;

4、stack

#include <stack>   //头文件size()     //返回栈元素个数empty()    //判空push()    //入栈pop()    //出栈top()    //返回栈顶元素

5、set/multiset

#include <set>    //头文件set去重,multiset不去重,均默认升序排序底层红黑树size()   //返回集合中元素个数empty()  //判空clear()  //清空所有元素insert()  //在集合中插入元素find()    //查找一个数,如果找到则返回该数第一次出现位置的迭代器,否则返回尾迭代count()    //返回某个值元素的个数

6、map/multimap

#include <map>    //头文件map去重,multimap不去重,均默认以key值(第一属性)升序排序底层红黑树size()   //返回map中元素个数empty()  //判空insert()  //插入元素find()    //同上count()   //同上

7、unordered_set/unordered_map

#include <unordered_set>  //头文件#include <unordered_map>    底层哈希 操作与set、map基本一致,参考上面即可

8、pair<int,int>

#include <utility>    //头文件first      //第一个元素second     //第二个元素//适用sort对其排序时默认以第一个元素升序排序

9、algorithm

#include <algorithm>    //头文件sort()      //传入首、尾地址(或首、尾迭代)排序,默认升序//若要降序排序或对结构体按其属性排序,需手写cmp函数bool cmp(int a,int b){     return a>b;}sort(a,a+n,cmp);//对结构体指定属性排序,例:struct Student{      string name;      double score;}stu[N];bool cmp(Student A,Student B){     return A.score>B.score;}//按成绩降序排序sort(stu,stu+n,cmp);max()       //取最大值min()       //取最小值swap()      //交换两个元素的值reverse()   //传参和sort一致,反转区间内的元素顺序unqiue()    //传参和和sort一致,去重相邻的相同元素,若原序列无序首先需排序,返回去重后原序列尾迭代

四、简单图论

1、单源最短路径

Dijkstra算法:求解边权均为正的单源最短路。

朴素版本:适用于稠密图(边数和点数平方一个数量级)

代码模板

int n;      //点数int g[N][N];    //邻接矩阵存储图 int dist[N];    //存储每个点到1号点的最短距离 bool st[N];    //存储每个点的最短路是否已经确定 int dijkstra(){memset(dist,0x3f,sizeof dist);dist[1]=0;for(int i=0;i<n;i++){    //迭代n次 int t=-1;for(int j=1;j<=n;j++){   //寻找距离最小的点 if(!st[j]&&(t==-1||dist[t]>dist[j])) t=j; } st[t]=true;      //标记为已确定  for(int j=1;j<=n;j++) dist[j]=min(dist[j],dist[t]+g[t][j]);   //用距离最小的点来更新其他点距离1号点的距离 }if(dist[n]==0x3f3f3f3f) return -3;    //最短路不存在 else return dist[n];                  //存在直接返回 }

选看:
堆优化Dijkstra算法:适用于稀疏图(边数和点数一个数量级)可以参考我的该篇博客

Spfa算法:求解存在负权边的最短路。

代码模板

int n,m;     //n表示点数,m表示边数 int h[N],e[M],ne[M],w[M],idx;   //邻接表存储图 int dist[N];       //存储每个点到1号点的最短距离 bool st[N];       //存储每个点是否已经在队列中 //邻接表加边 void add(int a,int b,int c){e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;}int spfa(){memset(dist,0x3f,sizeof dist);queue<int> q;dist[1]=0;st[1]=true;q.push(1);while(!q.empty()){int t=q.front();q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];if(!st[j]){st[j]=true;q.push(j);}}}}if(dist[n]==0x3f3f3f3f) return -3;else return dist[n];} 
参考例题:这里

2、多源最短路

注:若时间紧迫,优先记忆Floyd算法,也可解决单源最短路问题,只不过时间复杂度较高,可以用其获得部分分数。

Floyd算法:求解多源最短路。

代码模板

int n;       //点数int d[N][N];       //邻接矩阵存储图,算法结束后d[i][j]存储i、j之间的最短路径长度 for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j) d[i][j]=0;else d[i][j]=0x3f3f3f3f;}}void floyd(){for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){d[i][j]=min(d[i][j],d[i][k]+d[k][j]);}} }}//注意,若图中存在负权边,所以1号点无法到n号点,d[1][n]也可能被更新也可能则d[i][j]若大于0x3f3f3f3f/2即可认为最短路不存在 
参考例题:这里

3、最小生成树

Prim算法

代码模板

int n;      //点数 int dist[N];  //存储点到当前最小生成树的距离 int g[N][N];  //邻接矩阵存储每条边 bool st[N];   //存储每个点是否已经在生成树中 int prim(){memset(dist,0x3f,sizeof dist);int res=0;     //存储最小生成树边权重之和 for(int i=0;i<n;i++){int t=-1;for(int j=1;j<=n;j++){    //寻找距离当前生成树最小的点 if(!st[j]&&(t==-1||dist[t]>dist[j])) t=j;}if(i&&dist[t]==0x3f3f3f3f3) return 0x3f3f3f3f;    //如果距离最小的点的距离仍然是正无穷说明无最小生成树 if(i) res+=dist[t];for(int j=1;j<=n;j++) dist[j]=min(dist[j],g[t][j]);st[t]=true;}return res;    //返回最小生成树边权重之和即可 }
Kruakal算法

代码模板

int p[N];            //并查集父结点数组 int find(int x){     //并查集find操作 if(p[x]!=x) p[x]=find(p[x]);return p[x];}struct Edge{      //存储每条边 int a,b,w;}edges[M]; bool cmp(Edge A,Edge B){    //手写cmp,使sort能为结构体排序 return A.w<B.w; }int kruskal(){for(int i=1;i<=n;i++) p[i]=i;        //初始化并查集 sort(edges,edges+m,cmp);             //按边权从小到大排序 int res=0,cnt=0;                    //res记录最小生成树边权之和,cnt记录当前最小生成树种的边数 for(int i=0;i<m;i++){int a=edges[i].a,b=edges[i].b,w=edges[i].w;if(find(a)!=find(b)){           //最小边权的起点和终点a,b不在一个连通块则合并他们 p[find(b)]=find(a);res+=w;cnt++;}}if(cnt<n-1) return 0x3f3f3f3f;      //n个点,最小生成树的边应为n-1条,少于n-1说明没有最小生成树 else return res;}
参考例题:这里

五、动态规划

1、0-1背包

0-1背包问题:每件物品只有一个

代码模板

int dp[N];        //存储每个状态的最大价值 int v[N],w[N];    //v[]存储每个物品的体积,w[]存储每个物品的价值 int n,m;          //n为物品数,m为背包容积 for(int i=1;i<=n;i++){    //枚举每个物品 for(int j=m;j>=v[i];j--){   //逆序枚举背包体积 dp[j]=max(dp[j],dp[j-v[i]]+w[i]);}}
具体参考这里

2、完全背包

完全背包问题:每种物品有无限个

代码模板

int dp[N];        //存储每个状态的最大价值 int v[N],w[N];    //v[]存储每个物品的体积,w[]存储每个物品的价值 int n,m;          //n为物品数,m为背包容积 for(int i=1;i<=n;i++){    //枚举每个物品 for(int j=v[i];j<=m;j++){   //正序枚举背包体积 dp[j]=max(dp[j],dp[j-v[i]]+w[i]);}}

3、多重背包

多重背包问题:每种物品有不同数量

代码模板

int dp[N];        //存储每个状态的最大价值 int v[N],w[N],s[N];    //v[]存储每个物品的体积,w[]存储每个物品的价值,s[]存储每个物品的最大数量 int n,m;          //n为物品数,m为背包容积 for(int i=1;i<=n;i++){    //枚举每个物品 for(int j=m;j>=v[i];j--){   //逆序枚举背包体积 for(int k=0;k<=s[i]&&k*v[i]<=j;k++){dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);}}}
参考例题:这里

4、线性DP

DP没有固定模板,建议复习一下最长上升子序列最长公共子序列问题的解题思想。

总结

以上内容均为比较基础的内容,如果学有余力,可以多拓展一些,真正的强者不应止步于此,我去写周赛题解了,再见。最后希望我们都能取得自己心仪的成绩,我们一起加油,奥利给!

点击全文阅读


本文链接:http://m.zhangshiyu.com/post/61269.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1