赣南师范大学蓝桥杯校赛题解:

蓝桥杯校赛题解:

作者:gnnu 电信cdy
前言:此些题型较为经典,部分涉及高级数据结构和经典算法,希望大家好好掌握这些算法。如作者讲解有误,请评论指出谢谢!
校赛链接:https://ac.nowcoder.com/acm/contest/71584/
邀请码:2023gnnulanqiao

第一题:翻转(语法题)

第二题:?数位DP(简单题)

第三题:总之就是非常崇拜(遍历)

第四题:牛牛爱种树(模拟题)

第五题:小明想走出赣师大(BFS/DFS)

第六题:浅浅刷个题(模运算、哈希表)
力扣第一题两数之和的解法有些类似

第七题:帮小红构造01串(构造题)

第八题:老师速速来救我呜呜呜(模拟或并查集)

第九题:最小的素数和(试除法判定质数+DFS深度优先搜索 BFS广度优先搜索也行 DFS代码量会少些)

第十题:小明想逃出去玩原神(动态规划 一维DP)


第一题:翻转(语法题):

请添加图片描述

思路分析:

这题我们只要将这个数字的前k位倒着输出就行,如果倒着输出的第一位是0,我们就跳过,第一位不允许有前导0。所以我们可以将这个数字转换为字符串的形式即可很容易操作。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
    string s;
    int k;
    cin>>s>>k;
    bool flag=false;
    for(int i=k-1;i>=0;i--){
        if(s[i]=='0' && !flag)continue;
        flag=1;
        cout<<s[i];
    }//直接倒着输出不含前导0的后续数字
    for(int i=k;i<s.size();i++)cout<<s[i];//后面的顺序相同,直接输出即可
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t=1;
    //cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

第二题:?数位dp(简单题):


在这里插入图片描述

思路分析:

我们删除数位直到这个数位不含前导0的偶数或是删完即可。我们肯定不至于把它删完,其实我们只要保证个位是偶数即可,所以我们删除个位直到个位为偶数即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans=0,n;
void solve(){
    cin>>n;
    while(n){
        int t=n%10;//当前这个数的个位
        if(t%2)ans++;//个位是奇数,删掉,次数+1
        else break;//个位是偶数,不必继续删除,退出
        n/=10;//n把个位去掉
    } 
    cout<<ans;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t=1;
    //cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

第三题: 总之就是非常崇拜(遍历):在这里插入图片描述

思路分析:

此题我们要使得崇拜值最大,还可以随意指定讲解题目的顺序。那我们只讲那种能让崇拜值上升的题目就好啦,即难度大于y的知识点,我们直接遍历即可

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
int ans=0,n,x,y;
int a[N];
void solve(){
    cin>>n>>x>>y;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=n;i>=1;i--){
        if(a[i]>y)ans+=3;//崇拜值upup
    }
    cout<<ans;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t=1;
    //cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

第四题:牛牛爱种树(模拟):

思路分析:

此题根据数据规模来看,我们可以直接按照题意模拟出题,直接看代码即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
int ans=0,n,a,k,b,m;
int num[N];
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>num[i];//输入
    cin>>a>>k>>b;
    cin>>m;//第m天
    for(int i=1;i<m;i++){//注意要循环m-1次哦
        for(int j=1;j<=n;j++){
            num[j]+=a;//加a
            if(num[j]>k)num[j]=b;//超过k,修剪为b
        }
    }
    for(int i=1;i<=n;i++)cout<<num[i]<<" ";//输出
    
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t=1;
    cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

第五题:小明想走出赣师大(BFS或DFS):

思路分析:

此题就是从一个指定起点,判定能否从这个点出发到达终点。即我们从这个点开始迭代搜素即可,在每个当前点都判断一下能否走通上下左右四个方向,选能走的方向继续迭代搜索。我们划分一个标记flag,如果到达终点,flag标记为1。说明到达,在每一次走新迷宫,记得将flag重置为0。
我在这里给出DFS和BFS两种代码,大家选择合适的使用即可

代码

DFS:

#include<bits/stdc++.h>
using namespace std;
const int N=505;
char g[N][N]={0};
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};//方向数组
bool st[N][N];//标记这个点是否走过
int n,m;
bool flag=0;
void dfs(int x,int y){
    if(g[x][y]=='E'){
        flag=1;//到达啦
        return;//终止
    }
    
    for(int i=0;i<4;i++){
        int nx=x+dx[i],ny=y+dy[i];//下一步要走的地方
        if(st[nx][ny] || g[nx][ny]=='#' || nx<1 || nx>n || ny<1|| ny>m)
            continue;//超出边界或是已经走过的地方,我们不再继续搜索下去
        st[nx][ny]=true;
        dfs(nx,ny);
    }
}
void solve(){
    int stx,sty;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            cin>>g[i][j];//初始化迷宫
            if(g[i][j]=='S')
                stx=i,sty=j;//找到起点在哪里
        }
    
    dfs(stx,sty);//从起点开始深度优先搜索
    if(flag)
        cout<<"Yes";
    else 
        cout<<"No";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    while(cin >> n >> m){;
        flag=0;//重置为0
        memset(g, 0, sizeof g);
        memset(st, 0, sizeof st);//多组数据,给他初始化一下
        solve();
        cout<<endl;
    }
    
}

BFS:

#include<bits/stdc++.h>
using namespace std;
const int N=505;
char g[N][N]={0};
#define  x first
#define  y second
typedef pair<int,int> PII;
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};//方向数组
bool st[N][N];//标记这个点是否走过
int n,m;
bool flag=0;
void bfs(int stx,int sty){
    queue<PII> qe;//初始化坐标队列
    qe.push({stx,sty});
    st[stx][sty]=true;//标记搜寻过
  
    while(qe.size()){
        PII now=qe.front();//获取队列的队头
        qe.pop();//出队
        for(int i=0;i<4;i++){//往上下左右四个方向走
            int nx=now.x+dx[i],ny=now.y+dy[i];//下一步要走的地方
            if(st[nx][ny] || g[nx][ny]=='#' || nx<1 || nx>n || ny<1|| ny>m)
                continue;//超出边界或是已经走过的地方,我们不再继续搜索下去
            if(g[nx][ny]=='E'){
                flag=1;break;
            }//下一个点就是终点,直接break跳出
            
            st[nx][ny]=true;//这个点将被搜寻
            qe.push({nx,ny}); //否则将下一个点入队,继续迭代搜素          
    }
}
}
void solve(){
    int stx,sty;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            cin>>g[i][j];//初始化迷宫
            if(g[i][j]=='S')
                stx=i,sty=j;//找到起点在哪里
        }
    
    bfs(stx,sty);//从起点开始深度优先搜索
    if(flag)
        cout<<"Yes";
    else 
        cout<<"No";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    while(cin >> n >> m){;
        flag=0;//重置为0
        memset(g, 0, sizeof g);
        memset(st, 0, sizeof st);//多组数据,给他初始化一下
        solve();
        cout<<endl;
    } 
}

第六题:浅浅刷个题(哈希+模运算):

在这里插入图片描述

思路分析:

题意很清晰,小明只会刷每天总题量为k的倍数的题目,想让他最多刷几天。首先我们将所有的数字都模一下k,因为我们要为k的倍数,就让所有大于k的数变成模k的余数。
例如n=5 3,每天的题量要为3的倍数
数列=1 2 3 4 5 模k后—》 1 2 0 1 2
模k为0的数本身就是k的倍数,可以拿出来单独刷一天
其它的数就要考虑两两组合加起来能不能等于k了,我们可以用一个哈希表map来存储这个数的数目,mp[3]=2代表3这个数有2个,接下来好办了。
我们开始遍历数列中不为0的数字,我们从数字a开始,要判断k-a这个数是否存在,即mp[k-1]>0。组合后,这两个数要减1,因为已经用过了。思路有点像力扣第一题两数之和。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
#define int long long
map<int,int> mp;//哈希表存储这个数是否存在
int res=0;//答案
int a[N];
void solve(){
   int n,k;
   cin>>n>>k;
   int ans=0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        a[i]%=k;//先将a[i]%k
        if(a[i]==0)
            ans++;//本身就是k的倍数,拿来刷一天
        else
            mp[a[i]]++;//这个数的数目+1
    }
    
    for(int i=0;i<n;i++){
        if(a[i]==0)continue;//是0直接跳出
        if(mp[k-a[i]] && mp[a[i]] && a[i]!=k-a[i]){
            //当a[i]和mp[k-a[i]]都存在时
            ans++;
            mp[k-a[i]]--;
            mp[a[i]]--;
        }//如果两个数不相同的话
        
        if(a[i]==k-a[i] && mp[a[i]]>=2){
            //这两个数相同,加起来就是k
            ans++;//组合刷一天
            mp[a[i]]-=2;//这个数用掉了两个
        }//这个数与 k-这个数 相同的情况
        
    }
    cout<<ans;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    //cin>>t;
    
    while(t--){
        solve();
        cout<<endl;
    }
    return 0;
}

第七题:帮小红构造01串(构造题):

在这里插入图片描述

思路分析:

构造题,首先我们可以这样子构造。一个长度为n的串,有k个1,t对相邻1,即11。我们首先肯定要排除不能构造的情况然后再开始安心构造,

什么时候不能构造呢?假定我们有k个1好吧。我们要使得这些1分散成只有t对相邻1,所以我们构造几个例子可以发现我们需要添加k-t-1个0,比如将4个1分散成只有两对相邻1,我们至多加1个0,即11011。如果分散成一对相邻1,我们至多加两个0,即110101,同理0对,1010101,。
1的数目k个,0的数目至多k-t-1个,1和0的数目加起来小于n,就是无法构造。

k=4,我们可以这样子1111,可以11011,110101,10101010,我们可以发现当这些1越紧凑,相邻1的对就会越多。所有k个1最多能达到的相邻对数为k-1对,1111就是有三对1。
所以我们要有t对相邻1,只要让(t+1)个1凑在一起就能满足这个条件。
这个连续1段的长度为t+1,剩余1的数目为(k-(t+1)),当我们将这些1和0组合一下输出即可
如n=10,k=7,t=4,首先我们构造四对相邻1,
构造出“11111”,用掉五个1还剩2个1。但是相邻1已经够了,我们不能再出现相邻1,所以我们用0补充后为11111 0101。
这时候所有1都用完了,但是长度还不够,我们只要在末尾补0即可。
11111 0101 0

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
int ans=-1,n,k,t,now=0;
int a[N];
void solve(){
    cin>>n>>k>>t;
    //k个1最多能组成k-1个对
    //t对 代表可以有构造t+1个连续1  即t=2 可以构造出含111的子串
    if(2*k-t-1>n){
        cout<<-1;
        return;//即不能构造
    }
    for(int i=1;i<=t+1;i++){
        cout<<1; 
    }//输出连续t+1个1

    int last=k-(t+1);//表示剩余1的数目
    for(int i=0;i<last;i++){
       cout<<"01";
    }
    
    //当前长度为(t+1) +2*(k-(t+1))
    for(int i=2*k-1-t;i<n;i++)cout<<0;//末尾补0
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t=1;
    //cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

第八题: 老师速速来救我呜呜呜(dfs暴力或并查集):

思路分析:

说实话这题的解法可就多了,题意很简单和限制条件很少。我们可以用数组暴力搜索的方式去解决。

1.数组暴力/dfs:存储1的所有联系方式,再从1的所有联系方式出发,遍历所有1的联系人可以联系到的人,逐层搜索下去,直接找到n或是搜索完毕为止。

2.这道题的标准解法还是并查集,当数据规模比较大的时候,第一种解法就用不了了。并查集是处理不相交的集合的合并之前的问题的高级数据结构,这道题是应用的并查集的完美题型。建议大家学习并查集之后再做此道题(并查集找先祖、合并、路径压缩等),会有豁然开朗的感觉。直接给大家看下我的代码,并查集实现。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
int pre[55];
int find(int x){
    if(pre[x]==x)
        return x;
    return pre[x]=find(pre[x]);
}//寻根
void merge(int x,int y){
    int a=find(x),b=find(y);
    if(a!=b)
        pre[a]=b;
}//合并
void solve(){
     for(int i=1;i<=n;i++){pre[i]=i;}//并查集初始化
     for(int i=1;i<=m;i++){
         int a,b;
         cin>>a>>b;
         if(a!=n && b!=1)
            merge(a,b);//防止老师有小明电话的情况出现,不然使得小明直接成为老师的代表元
     }
    if(find(1)==find(n))
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
    return;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    while(cin>>n>>m){
        solve();
    }
    return 0;
}

第九题:最小的素数和(dfs+判定质数):

在这里插入图片描述

思路分析:

拿到这个题后,我们首先观察一下数据规模。emmm,很好,n<=10。最多只有十个数字,让我们找每个数字的素因子最小和且素因子间不能重复。
数据规模小,找最优结果,我们一下子就能想到用DFS搜索或是BFS搜素。这里我们采用dfs(代码量比bfs少),这里的a[i]小于1000,我们可以直接遍历a[i],如果a[i]较大,我们只需要遍历根号a[i]即可。
写一个试除法判定质数,如果数据规模较大,我们可以使用线性筛来判定质数。这题我们使用试除法判定质数和遍历整个a【i】就行,上代码。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
int n,ans=1e9+10;
map<int,int> mp;//代表这个数是否存在
int a[1005]={0};
bool check(int x){
    if(x<2)return false;
    
    for(int i=2;i<=sqrt(x);i++){
        if(x%i==0)return false;
    }//不是质数
    return true;//是质数
}
void dfs(int pos,int last){
    //检查pri+a[pos]的数字的素数和
    //pos表示当前再操作第pos个数
    //last表示在这个数字之前的素数和
    if(last>=ans)return;
    //当前总和已经大于暂时的ans值,在搜下去也不会再优了,剪枝,优化时间复杂度
    if(pos>n){
        //cout<<last<<endl;
        ans=min(ans,last);//更新
        return;
    }//如果遍历完,则结算
    
    int now=a[pos];
    for(int i=2;i<=now;i++){
        if(now%i==0){
            if(check(i) && mp[i]==0){
                mp[i]++;//这个素数存在
                dfs(pos+1,last+i);//继续向下一层迭代搜索
                mp[i]--;//回溯状态
            }
        }
    }
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    dfs(1,0);//开始深度优先搜索
    if(ans==1e9+10)
        cout<<-1;
    else
        cout<<ans;
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

小竹想逃出去玩原神(一维动态规划):

在这里插入图片描述

思路分析:

简单动态规划即可,dp[i] 表示 选到第i个物品为止所能凑出的最长绳子。
没有了解动态规划先了解一下01背包模型。
从第一个物品开始dp,我们知道选了i后,i+1,i+2,… i+k这些物品都不能被选择。
故当i<k时,dp【i】一直都是选一个物品能达到的最长长度。
当i>=k时,dp[i]=max(dp[i-k-1]+a[i],dp[i-1]);即不选这个物品最长的最长长度和选择这个物品后长度,两者中的较大值。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
int ans=-1,n,k;
int a[N],dp[N]={0};
void solve(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(i<=k+1){dp[i]=max(dp[i-1],a[i]);}
        else dp[i]=max(dp[i-k-1]+a[i],dp[i-1]);
        ans=max(ans,dp[i]);
    }
    cout<<ans;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
        cout<<endl;
    }
}

结尾:作者:gnnu 电信cdy
此些较为基础,部分涉及高级数据结构和经典算法,希望大家好好掌握这些算法。如作者讲解有误,请评论指出谢谢!

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值