2020 第十一届蓝桥杯大赛软件赛决赛,国赛,C/C++大学B组题解

简介: 2020 第十一届蓝桥杯大赛软件赛决赛,国赛,C/C++大学B组题解

@[toc]

试题地址:https://www.lanqiao.cn/courses/2786/learning/?id=131138
补题地址:http://lx.lanqiao.cn/problemsets.page

第1题——美丽的2 (5分)

  • 题目:
  • 直接暴力1-2020,然后判断一下有没有2即可。
  • 答案是563

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        int res = 0;
        for(int i = 1; i <= 2020; i++){
            int x = i, cc = 0;
            while(x){
                if(x%10==2)cc=1;
                x/=10;
            }
            if(cc)res++;
        }
        cout<<res<<"\n";
        return 0;
    }
    
    

第2题——扩散 (5分)

  • 题目
  • 思路:

从四个点开始往外bfs,扩散2020轮即可。
对于坐标负数的情况,默认加上2020,相当于离散化处理。
注意边界处理

  • 答案是20312088

    #include<bits/stdc++.h>
    using namespace std;
    
    struct node{ int x, y, t = 0; };
    int vis[2020+2020+2020][2020+2020+2020];
    node getnode(int x, int y){ return node{x+2020,y+2020,0}; }
    int dx[] = {0,0,-1,1};
    int dy[] = {1,-1,0,0};
    
    int main(){
        queue<node>q;
        q.push(getnode(0,0));
        q.push(getnode(2020,11));
        q.push(getnode(11,14));
        q.push(getnode(2000,2000));
        int ans = 0;
        while(q.size()){
            node t = q.front();  q.pop();
            vis[t.x][t.y] = 1;
            ans++;
            // cout<<t.x<<' '<<t.y<<" "<<t.t<<"\n";
            // if(t.t==5)break;
            for(int i = 0; i < 4; i++){
                int nx = t.x+dx[i], ny = t.y+dy[i];
                if(vis[nx][ny]==0 && nx>=0&&ny>=0&&t.t+1<=2020){//边界
                    q.push({nx,ny,t.t+1});
                    vis[nx][ny] = 1;
                }
            }
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    

第3题——阶乘约数 (10分)

  • 题目:
  • 思路

由质因数唯一分解定理可得,100!分解质因数后表示唯一,所以的因数个数+1乘起来就是它的因数个数。所以直接枚举1-100,累加质因数,最后统计即可。
记得开ll不然会炸。

  • 答案:39001250856960000

    #include<bits/stdc++.h>
    using namespace std;
    int c[200];
    
    int main(){
        for(int i = 2; i <= 100; i++){
            int x = i;
            for(int j = 2; j*j<=i; j++){
                while(x%j==0){
                    x /= j;
                    c[j]++;
                }
            }
            if(x!=1)c[x]++;
        }
        long long ans = 1;
        for(int i = 2; i <= 100; i++){
            ans *= (c[i]+1);
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    

第4题——本质上升序列 (10分)

  • 题目
  • 思路:给出一个字符串,求有多少个不重复的单调递增子串
  • 类似于最长上升子序列,区别在于不能重复,而且求的是个数。

记f[i]表示以s[i]结束的不重复单调递增子串个数,初始值为1即本身,转移时可以从前面所有比他小的字符转移过来,累加即可。
对于重复的情况,可以在转移时枚举到前面有和当前字符相同字符时把转移过的值记为0。

  • 所以答案是3616159

    #include<bits/stdc++.h>
    using namespace std;
    
    int f[500];
    
    int main(){
        string s="tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
        for(int i = 0; i < s.size(); i++)f[i] = 1;
        for(int i = 0; i < s.size(); i++){
            for(int j = 0; j < i; j++){
                if(s[i]>s[j])f[i] += f[j];
                if(s[i]==s[j])f[i] = 0;
            }
        }
        int ans = 0;
        for(int i = 0; i < s.size(); i++){
            ans += f[i];
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    

第5题——玩具蛇 (15分)

  • 题目:
  • 思路:

乍一看没啥头绪,但是考虑到只有16,方案数比较少,所以可以暴力dfs搜。
暴力每个起点,每次往四个方向搜+1,然后对于不同的终点答案+1。

  • 所以答案是552

    #include<bits/stdc++.h>
    using namespace std;
    int n = 4, ans = 0;
    int vis[10][10];
    
    int dx[] = {0,0,-1,1};
    int dy[] = {1,-1,0,0};
    void dfs(int x, int y, int now){
        if(now==n*n){
            ans++; return ;
        }
        for(int i = 0; i < 4; i++){
            int nx = x+dx[i], ny = y+dy[i];
            if(nx>=1&&nx<=n&&ny>=1&&ny<=n &&vis[nx][ny]==0){
                vis[nx][ny] = 1;
                dfs(nx,ny,now+1);
                vis[nx][ny] = 0;
            }
        }
    }
    
    int main(){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                memset(vis,0,sizeof(vis));
                vis[i][j] = 1;
                dfs(i,j,1);
            }
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    

第6题——皮亚诺曲线距离 (15分)

  • 题目
  • 思路:

说实话这道题目放在第六题感觉有点不讲武德,感觉比后面的难好吧()
求两点距离不难想到可以转化为两点到起点的距离作差。

  • 然后,考虑简化问题,k阶的图是由9个k-1阶的图组成的

我们可以考虑它在这九个中的哪一块,然后分类讨论递归下去,直到来到1阶和2阶,输出答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

LL calc(LL x, LL n){
    LL ans = 1;
    while(n){
        if(n%2==1){ ans*=x; n--;}
        x *= x;  n/= 2;
    }
    return ans;
}
LL dfs(LL now, LL xx, LL yy){
    if(now==0)return 1;
    if(now>=40)now=39;
    //(n-1)阶时的边长,也就是n阶时,分成9个(n-1)阶时每一块的边长
    LL k = calc(3,now-1); 
    //判断该点在所在块内的横纵坐标(x,y), 以及所在块的横纵坐标(a,b)
    LL x = xx%k, y = yy%k, a = xx/k, b = yy/k;
    if(a==0&&b==0)     return 0*k*k+dfs(now-1,x,y);
    else if(a==0&&b==1)return 1*k*k+dfs(now-1,k-1-x,y);
    else if(a==0&&b==2)return 2*k*k+dfs(now-1,x,y);
    else if(a==1&&b==2)return 3*k*k+dfs(now-1,x,k-1-y);
    else if(a==1&&b==1)return 4*k*k+dfs(now-1,k-1-x,k-1-y);
    else if(a==1&&b==0)return 5*k*k+dfs(now-1,x,k-1-y);
    else if(a==2&&b==0)return 6*k*k+dfs(now-1,x,y);
    else if(a==2&&b==1)return 7*k*k+dfs(now-1,k-1-x,y);
    else if(a==2&&b==2)return 8*k*k+dfs(now-1,x,y); 
}

int main(){
    LL n;  cin>>n;
    LL x1, y1, x2, y2;  cin>>x1>>y1>>x2>>y2;
    LL ans = dfs(n,x1,y1)-dfs(n,x2,y2);
    if(ans<0)ans=-ans;
    cout<<ans<<"\n";
    return 0;
}

第7题——游园安排 (20分)

  • 题目
  • 思路:

看完就是个LIS最长上升子序列,只不过把数字换成了字符串比较。
对于路径打印,开个数组回溯一下即可。
暴力dp能过70%

//70分做法
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;

int cnt = 0;
string name[maxn];
int f[maxn]; //到i为止的最大值

int main(){
    string s;  cin>>s;
    for(int i = 0; i < s.size(); i++){
        if(isupper(s[i])){
            name[++cnt]="";
            name[cnt]+=s[i];
        }else{
            name[cnt]+=s[i];
        }
    }
    int mxl = 0, pos = 1;  //记录最长序列的长度和坐标
    for(int i = 1; i <= cnt; i++){
        f[i] = 1;
        for(int j = 1; j < i; j++){
            if(name[i]>name[j]){//可以从比i小的转移过来
                f[i] = max(f[i], f[j]+1);
            }
        }
        if(f[i]>=mxl){
            mxl = f[i], pos = i;
        }
    }
    string res = "";
    for(int i = pos; i >= 1; i--){//向前回溯
        if(f[i]==mxl){
            res = name[i]+res;
            mxl--;
        }
    }
    cout<<res<<"\n";
    return 0;
}

  • 考虑贪心优化

将所有人序列取出存储到数组中后,先将第一个序列入队,遍历剩下所有序列,如果说碰见比队尾大的序列就直接入队,否则,也就是当前序列并不小于队尾序列的话,那么就用其代替掉队列中比其大的第一个序列,至于如何找到这个第一个比他大的,因为队列是有序的,那么就可以二分,复杂度从平方优化到了log。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;

#define IOS ios::sync_with_stdio(0), cin.tie(0),cout.tie(0)
int cnt = 0;
string name[maxn];
vector<int>f; //到i为止的最大值

int main(){
    IOS;
    string s;  cin>>s;
    for(int i = 0; i < s.size(); i++){
        if(isupper(s[i])){
            if(i!=0)cnt++;
            name[cnt]="";
            name[cnt]+=s[i];
        }else{
            name[cnt]+=s[i];
        }
    }
    vector<string>vc; //当前LIS序列
    vc.push_back(name[0]);
    f.push_back(1);
    for(int i = 1; i <= cnt; i++){
        if(name[i] > vc.back()){//能构成就直接加
            vc.push_back(name[i]);
            f.push_back(vc.size());
        }else{//不能就去二分第一个小于name[i]的位置替换掉(肯定更优)
            int pos = lower_bound(vc.begin(), vc.end(), name[i])-vc.begin();
            vc[pos] = name[i];
            f.push_back(pos+1);
        }
    }
    // string res = "";
    vector<string>res;
    int len = vc.size();
    for(int i = cnt; len>0; i--){//向前回溯
        if(f[i]==len){
            res.push_back(name[i]);
            // res = name[i]+res;
            len--;
        }
    }
    // cout<<res<<"\n";
    for(int i = res.size()-1; i >= 0; i--){
        cout<<res[i];
    }
    return 0;
}

第8题——答疑 (20分)

  • 题目
  • 思路:

贪心策略为,a+s+e越小的排在越前面。证明过程参考如下。

#include<bits/stdc++.h>
using namespace std;
struct node{ int s, a, e; }p[1500];
bool cmp(node x, node y){ return x.s+x.a+x.e<y.s+y.a+y.e; }
int main(){
    int n;  cin>>n;
    for(int i = 1; i <= n; i++)
        cin>>p[i].s>>p[i].a>>p[i].e;
    sort(p+1,p+n+1,cmp);
    long long ans = 0;
    for(int i = 1; i <= n; i++){
        ans += (n-i)*(p[i].a+p[i].e+p[i].s)+p[i].a+p[i].s;
    }
    cout<<ans<<"\n";
    return 0;
}

第9题——出租车 (25分)

  • 题意:
  • 思路:

最短路,Dijkstra或spfa,代码太长不想写了,
贴个其他dalao的题解把

第10题——质数行者 (25分)

  • 题意:
  • 思路

oh,高维dp,不想写了
贴个其他dalao的题解把

目录
相关文章
|
7月前
|
存储 缓存 监控
用 C++ 红黑树给公司电脑监控软件的日志快速排序的方法
本文介绍基于C++红黑树算法实现公司监控电脑软件的日志高效管理,利用其自平衡特性提升日志排序、检索与动态更新效率,并结合实际场景提出优化方向,增强系统性能与稳定性。
208 4
|
10月前
|
存储 机器学习/深度学习 算法
基于 C++ 的局域网访问控制列表(ACL)实现及局域网限制上网软件算法研究
本文探讨局域网限制上网软件中访问控制列表(ACL)的应用,分析其通过规则匹配管理网络资源访问的核心机制。基于C++实现ACL算法原型,展示其灵活性与安全性。文中强调ACL在企业与教育场景下的重要作用,并提出性能优化及结合机器学习等未来研究方向。
260 4
|
负载均衡 算法 安全
探秘:基于 C++ 的局域网电脑控制软件自适应指令分发算法
在现代企业信息化架构中,局域网电脑控制软件如同“指挥官”,通过自适应指令分发算法动态调整指令发送节奏与数据量,确保不同性能的终端设备高效运行。基于C++语言,利用套接字实现稳定连接和线程同步管理,结合实时状态反馈,优化指令分发策略,提升整体管控效率,保障网络稳定,助力数字化办公。
257 19
|
算法 安全 C++
用 C++ 算法控制员工上网的软件,关键逻辑是啥?来深度解读下
在企业信息化管理中,控制员工上网的软件成为保障网络秩序与提升办公效率的关键工具。该软件基于C++语言,融合红黑树、令牌桶和滑动窗口等算法,实现网址精准过滤、流量均衡分配及异常连接监测。通过高效的数据结构与算法设计,确保企业网络资源优化配置与安全防护升级,同时尊重员工权益,助力企业数字化发展。
294 4
|
算法 C++
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
446 5
|
安全 网络安全 数据安全/隐私保护
探索企业上网行为管理软件:C++ 的视角
在数字化企业环境中,上网行为管理软件至关重要,它不仅保障信息安全还优化网络资源分配。C++以高效和强大性能为基础,支持这类软件的开发。通过示例代码展示了如何使用C++捕获网络数据包、控制特定网址访问及分析网络流量模式,展现了C++在处理大规模网络数据方面的优势,满足企业对网络安全与管理的需求。
215 1
|
存储 算法 测试技术
第十五届蓝桥杯大赛 国赛 pb组F题【括号与字母】(15分) 栈的应用
第十五届蓝桥杯大赛 国赛 pb组F题【括号与字母】(15分) 栈的应用
204 1
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
12月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
466 12
|
10月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
250 0