蓝桥杯试题(真题)训练
真题题解:https://blog.csdn.net/qq_42835910/article/details/88959695
1. 小计算器 运算符 难度:5颗星
c语言的compare函数比较两字符串大小
1 //使用c语言的compare函数比较两字符串大小 返回值为-1,0,1 2 string s1 = "asd",s2 = "asd"; 3 int t = s1.compare(s2);//逐个比较字母,若t=-1,说明 s1<s2 ; 若t=1,说明 s1>s2 ; 若t=0,说明 s1=s2 ; 4 cout<<t<<endl;
思路:
使用两个函数:toNumber(string s,int c)、toString(long long n,int c) 对于toNumber(string s,int c)函数作用:将c进制的s2字符串转为10进制的数字 对于toString(long long n,int c)函数作用:将10进制的n数字转为c进制的字符串 对于该题,我们运算中的数字都转为10进制去运算,所以我们的数字平时都以10进制保存(输入s2之后,使用toNumber(s2,c)转为10进制) 当我们需要输出答案时,再将运算结果用c进制输出(toString(res,c))
代码:
注明:易错点都已在源代码中标出。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; //将c进制的s字符串转换为10进制的数字 long long toNumber(string s,int c){ //第四处:返回数据应为long long型 int len = s.length(); long long n = 0; for(int i = 0;i<len;i++){ if(s[i] >= 'A') n = (s[i]-'A'+10) + n*c; else n = (s[i]-'0') + n*c; } return n; } //将10进制的数字n转换为c进制的字符串s string toString(long long n,int c){ string s; if(n == 0) return "0"; //第一处:忘记考虑n=0的情况 while(n){ long long t = n%c; //第二处:t的数据范围错误写成int if(t>=10) s.push_back(t-10+'A'); else s.push_back(t+'0'); n/=c; } reverse(s.begin(),s.end()); return s; } int main(){ //n表示操作数,c表示当前进制单位,isclear标记前面的数是否被清除 //p表示操作符序号 int n,c=10,isclear = 1,p=0; cin>>n; //s1表示操作,s2表示数字 string s1,s2; long long res,num; while(n--){ cin>>s1; if(!s1.compare("NUM")){ cin>>s2; if(isclear){//说明前面的数已经被清除 res = toNumber(s2,c);//说明s2为c进制的数,将s2转为10进制的数字 isclear = 0; //第六处:记得将isclear的值设为0 } else{ //说明前面有数 num = toNumber(s2,c); if(p==1) res+=num; else if(p==2) res-=num; else if(p==3) res*=num; else if(p==4) res/=num; else if(p==5) res%=num; p=0; //第7处:记得将p的值设为0 } } else if(!s1.compare("ADD")){ p=1; } else if(!s1.compare("SUB")){ p=2; } else if(!s1.compare("MUL")){ p=3; } else if(!s1.compare("DIV")){ p=4; } else if(!s1.compare("MOD")){ p=5; } else if(!s1.compare("CHANGE")){ cin>>c; } else if(!s1.compare("EQUAL")){ //平时计算都以10进制,输出将res化为c进制 cout<<toString(res,c)<<endl; //第三处:输出没加换行 } else if(!s1.compare("CLEAR")){//表示将前面的数清除 isclear = 1; //第五处:总容易误写成将isclear=1写成c=1,导致样例输出结果为40 } } return 0; }
2.合根植物 求“连通块数目”题 tarjan算法或者并查集 需复习
并查集解法:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; //并查集算法 find and union函数 #define maxn 1000010 int pre[maxn]; int m,n,k,cnt; void Init(){ for(int i = 1;i<=m*n;i++){ pre[i]=i; } cnt = m*n; //cnt表示刚开始的根节点数目 } int myFind(int a){ if(a == pre[a]) return a; else return pre[a]=myFind(pre[a]); } void myunion(int a,int b){ int aroot = myFind(a); int broot = myFind(b); if(aroot != broot){ pre[broot] = aroot;//易错点:需注意,不要写成pre[b]=a; cnt--; } } int main(){ cin>>m>>n; Init(); cin>>k; while(k--){ int a,b; cin>>a>>b; myunion(a,b); } cout<<cnt<<endl; }
3.分考场 回溯搜索 每次可以进行两种操作:新建考场 或者 某一个之前的考场 需复习
题解:https://www.cnblogs.com/Aiahtwo/p/12267118.html
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 6 #define maxn 105 7 int n,m,ans=99999; 8 int f[maxn][maxn]={0}; 9 int a[maxn]={0};//a[i]存第i个人所在的考场 10 11 bool Judge(int x,int room){ //判断第x个人是否能使用第room个考场 12 for(int i=1;i<=n;i++){ 13 if((f[x][i]==1)&&(a[i]==room)){ //说明该考场存在与x有关系的人 14 return false; 15 } 16 } 17 return true; 18 } 19 20 void solve(int x,int room_num){ 21 if(room_num>=ans){ //剪枝 注意:我这里如果少了=,评测机会判断超时... 22 return; 23 } 24 if(x>n){ //说明安排考场任务结束 25 ans=min(ans,room_num); 26 return; 27 } 28 //判断是否可以使用之前的某个考场 29 for(int i=1;i<=room_num;i++){ 30 if(Judge(x,i)==true){ 31 a[x]=i; 32 solve(x+1,room_num); 33 a[x]=0; 34 } 35 } 36 //前面那些考场无法使用,需新建考场 37 a[x]=room_num+1; 38 solve(x+1,room_num+1); 39 a[x]=0; 40 } 41 42 int main(){ 43 int x,y; 44 cin>>n>>m; 45 while(m--){ 46 cin>>x>>y; 47 f[x][y]=f[y][x]=1; 48 } 49 50 solve(1,0); 51 cout<<ans<<endl; 52 }
4.小数第n位 思维题:模拟除法 需复习
注意:我们需判断是否需要对代码进行优化,不优化是否会超数据范围,超时之类的...
很明显,该题需进行优化,对数据逼近防止超时
---------易错点:注意变量的数据类型,int与long long的区别
#include <iostream> using namespace std; int main() { long long i,a,b,n; cin>>a>>b>>n; a=a%b; //若n过大,加一个while快速逼近小数点后第n位的位置 while(n>10) { a*=1e10; n-=10; a%=b; } for(i=1;i<=n+2;i++) //注意:i的数据类型 { a*=10; if(i>=n){ cout<<a/b; } a%=b; } return 0; }
二分查找方法:这个要记住背诵
参考博客:https://www.cnblogs.com/wkfvawl/p/9475939.html
binary_search(a,a+N,t)==true//表明在数组a中存在t这个数字 下标=lower_bound(a,a+N,t)-a;//得到>=t这个数的下标
5.对局匹配 需复习
算法思想:动态规划
求:最多可以存在多少名(任意两名用户积分差不等于K)
我们对用户的积分进行整理(对k取余,余数相同的积分在同一分组):
0,0+k,0+2k...
1,1+k,1+2k...
2,2+k,2+2k...
只有在同一个分组的用户才可能 积分差等于K
所以我们先求出每一个分组的 最多可存在用户人数
最后总和分组得到答案
对每一个分组进行状态转移方程:
f[j]=max(f[j-1],f[j-2]+num[j]);
f[j]:表示 [该分组] 到第i个元素的最大用户人数
对于积分i我们选或者不选完全取决于它的用户个数,而如果选积分i,那么积分i-k与积分i+k是不可选的,而如果不选积分i,那么积分i-k与积分i+k是可选的,
所以:num[i]=max(num[i-k],num[i]+num[i+k])
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 #define maxn 100001 5 int n,k,ans; 6 //hsh[i]表示积分为i的用户人数 7 //下面两个数组每在一个分组中就重新赋值; 同一分组的余数t相同:t,k+t,2*k+t,3*k+t... 8 //num[i]存该分组的第i个元素的用户人数 9 //f[i]存该分组到第i个元素的最大用户人数 , 最大用户人数的关系:a,a+k,a+2*k , max(num[a]+num[a+2*k],num[a+k]) 10 int hsh[maxn]={0},num[maxn]={0},f[maxn]={0}; 11 int main(){ 12 cin>>n>>k; 13 ans=0; 14 int t,i,j; 15 for(i=0;i<n;i++){ 16 cin>>t; 17 hsh[t]++; 18 } 19 if(k==0){//重复积分只取一个 20 for(i=1;i<maxn;i++){ 21 if(hsh[i]!=0){ 22 ans++; 23 } 24 } 25 } 26 else{ 27 for(i=0;i<k;i++){//k个分组 28 int m=1; 29 for(j=i;j<maxn;j+=k){ //j为该分组的积分值,范围在(i,maxn) 30 num[m++]=hsh[j];//hsh[j]为积分值为j时的用户人数 31 } 32 f[1]=num[1]; 33 for(j=2;j<=m;j++){ //注意:这里的j一定要从2开始,否则下面的j-2会下标越界 34 f[j]=max(f[j-1],f[j-2]+num[j]); 35 } 36 ans+=f[m]; 37 } 38 } 39 cout<<ans<<endl; 40 return 0; 41 }
2.递推写法
#include<iostream> #include<algorithm> using namespace std; #define maxn 100001 int has[maxn]={0},d[maxn]={0}; int main(){ int n,k; cin>>n>>k; for(int i=0;i<n;i++){ int x; cin>>x; has[x]++; } int ans=0; if(k==0){ for(int i = 1; i < maxn; i++) ans += (bool)has[i]; } else{ for(int i=0;i<k;i++){//k个分组 int sum=0; d[i]=has[i],d[i+k]=has[i+k]; for(int j=i+2*k;j<maxn;j+=k){ d[j]=max(d[j-k],d[j-2*k]+has[j]);//递推关系式 sum=max(d[j],sum); } ans+=sum; } } cout<<ans<<endl; return 0; }
6.发现环 并查集+dfs
思路:并查集找到环上的某点之后,进行递归求该环上的所有点
代码:
#include <cstdio> #include<iostream> #include <algorithm> #include<vector> using namespace std; const int maxn = 100001; int n; int f[maxn],flag,vis[maxn]={0}; vector<int> g[maxn],ans; int Find(int x){ return f[x]==x?x:f[x]=Find(f[x]); } bool dfs(int x){ if(vis[x]){ if(x==flag){ //说明递归结束 return true; } else{ //说明回溯到前面经过的某节点 return false; } } vis[x]=1; for(int i=0;i<g[x].size();i++){ if(dfs(g[x][i])){ ans.push_back(g[x][i]); return true; } } return false; } int main(){ cin>>n; int x,y; //并查集一定要记得初始化 for(int i=0;i<n;i++) f[i]=i; for(int i=0;i<n;i++){ cin>>x>>y; g[x].push_back(y); g[y].push_back(x); int rootx=Find(x), rooty=Find(y);//并查集找到环的某点 if(rootx!=rooty){ f[rootx]=rooty; } else{//两个祖宗节点相同,说明是环节点 flag=x; dfs(x);//找到环上的点后进行递归 } } sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) cout<<ans[i]<<" "; return 0; }
7. 翻硬币 简单题
#include<cstdio> #include<iostream> #include<cstring> using namespace std; #define maxn 1001 int main(){ char a[maxn],b[maxn]; scanf("%s%s",&a,&b); int len=strlen(a),cnt=0; for(int i=0;i<len;i++){ if(a[i]==b[i]) continue; else{ //遍历过去,发现两个字符不相等的话,操作加一,把后面字符b[i+1]反转即可 cnt++; if(b[i+1]=='o') b[i+1]='*'; else b[i+1]='o'; } } cout<<cnt<<endl; return 0; }
7.错误票据 简单题 读题+简单哈希
#include<iostream> using namespace std; #define maxn 100001 int has[maxn]={0}; int main(){ int n,x; cin>>n; int A=maxn,B=0; for(int i=0;i<n;i++){ while(cin>>x){ has[x]++; if(x<A) A=x; if(x>B) B=x; char ch = getchar(); if(ch=='\n') break; } } int d,c;//从题意可知,只有一个断数和重复数,总的数目为B-A+1个数字 for(int i=A;i<=B;i++){ if(has[i]==2) c=i;//hash值为2.说明这个数有重复 if(has[i]==0) d=i;//hash值为0.说明这个数没有输入过 } cout<<d<<" "<<c<<endl; return 0; }
-------------------continue--------------------

浙公网安备 33010602011771号