蓝桥杯试题(真题)训练

真题题解: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;
} 
View Code

 

 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--------------------

posted @ 2020-02-04 16:58  saaas  阅读(344)  评论(0)    收藏  举报