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

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

@toc

官方题目链接:
https://www.lanqiao.cn/courses/2786/learning/?id=70881

第1题——平方序列 (5分)

  • 题目:
  • 思路:

2x^2 = 2019^2+y^2, 从2020开始枚举x, 用公式算出y,如果是个正数就是最小的。

  • 答案为7020
#include<bits/stdc++.h>
using namespace std;

int main(){
    for(int x = 2020; x <= 9999; x++){
        int y2 = 2*x*x-2019*2019;
        int y = sqrt(y2);
        if(y*y==y2){
            cout<<x+y<<"\n";
            return 0;
        }
    }
    return 0;
}

第2题——质数拆分 (5分)

  • 题意:
  • 思路:

先把1-2019的质数都筛出来,然后作为体积Vi,跑容量为2019的背包。
fij表示到第i个素数为止,拼成j的方法数。
记得不开ll会爆。

  • 答案是55965365465060

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn = 5050;
    
    //线性筛
    LL vis[maxn], primes[maxn], cnt;
    void get_primes(int n){
        for(int i = 2; i <= n; i++){
            if(!vis[i])primes[++cnt]=i;
            for(int j = 1; primes[j] <= n/i; j++){
                vis[primes[j]*i] = 1;
                if(i%primes[j]==0)break;
            }
        }
    }
    
    LL f[maxn][maxn];
    
    int main(){
        get_primes(maxn-10);
        f[0][0] = 1;
        for(LL i = 1; i <= cnt; i++){
            for(LL j = 0; j <= 2019; j++){
                f[i][j] = f[i-1][j];
                if(j>=primes[i])f[i][j] += f[i-1][j-primes[i]];
            }
        }
        cout<<f[cnt][2019];
        return 0;
    }
    
    

第3题——拼接 (10分)

  • 题目
  • 思路:

找规律,发现除了沿右边界分割的情况,右上角那一个必选。
然后第一行可以选1-7的格子,第二行能选1-6,后面依次类推
且右端一定靠近对角线,下一行不能多余上一行,上下两行要连续。
然后dfs

  • 所以答案是128

    #include<bits/stdc++.h>
    using namespace std;
    
    int a[50], n = 7;
    int dx[] = {0,0,1,-1};
    int dy[] = {1,-1,0,0};
    int ans = 1;
    
    void dfs(int cur, int now){
        if(cur>=n || now==0){
            ans++;
            for(int i = 1; i <= n; i++)cout<<a[i]<<' '; cout<<"\n";
            return ;
        }
        for(int i = 0; i<now && i<=n-cur; i++){
            a[cur+1] = i;
            dfs(cur+1, i);
            a[cur+1] = 0;
        }
    }
    
    int main(){
        for(int i = 1; i <= n; i++){
            memset(a,0,sizeof(a));
            a[1] = i;
            dfs(1,i);
        }
        cout<<ans;
        return 0;
    }
    
    

第4题——求值 (10分)

  • 题意:
  • 思路
    数字有点小,,才100,暴力枚举每个数,求约数个数,第一个有100个约数的数肯定很小呀,然后输出就好了
  • 答案是45360

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        for(int i = 100; ;i++){
            int cnt = 0;
            for(int j = 1; j <= i; j++){
                if(i%j==0)cnt++;
            }
            if(cnt==100){
                cout<<i<<"\n";
                return 0;
            }
        }
        return 0;
    }
    
    

第5题——路径计数 (15分)

  • 题意:
  • 思路:

直接0,0开始往外dfs走12步看看有没有回来即可。
对于自交的情况,因为走过的路不会往回走,所以不需要考虑。
!!!但是有一个特判没考虑到,因为vis是回溯当前的点不能走,但是我往右走一步可以直接往回走到起点,这个时候vis是不会影响到原点的,所以会炸,要减去这两个值。

  • 所以答案是206

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

第6题——最优包含 (15分)

  • 题意:
  • 思路:给出字符串S和T,求S修改多少个字母能包含T。
  • 类似于LCS,令fi表示S串前i个字符,包含T串前j个字符所需要的最小修改次数。

分四种情况转移。反正nm范围小,随便操作。

#include<bits/stdc++.h>
using namespace std;
int f[1010][1010];
int main(){
    string a, b;  cin>>a>>b;
    int n = a.size(), m = b.size();
    a = "0"+a, b = "0"+b;
    memset(f,0x3f,sizeof(f));
    f[0][0] = 0;
    for(int i = 1; i <= n; i++){
        f[i][0] = 0;
        for(int j = 1; j <= m; j++){
            f[i][j] = f[i-1][j]; //s[i-1]改到t[j]的最值,不用s[i]这个字符
            if(a[i]==b[j])f[i][j] = min(f[i][j], f[i-1][j-1]);//s[i-1]改到t[j-1],s[i]与t[j]配对
            else f[i][j] = min(f[i][j], f[i-1][j-1]+1);//修改一个,增加一次次数
        }
    }
    cout<<f[n][m]<<"\n";
    return 0;
}

第7题——排列数 (20分)

  • 题意:
  • 思路:

求1-n的排列中,有多少个是k单调的,k单调指的是有k-1个数比两边的大或小。
dp,记f(i,j)表示第i个数,j个折点的方案数。

  • 转移时考虑将第i个数放入1~i-1的排列中,对折点数量的影响。

分类讨论插入的情况。
如果插入单调序列中,折点数量+2,波峰和波谷各一个。
如果插入波峰或波谷的两端,折点数量不变。
如果插入波峰或波谷的重心,折点数量+2。
如果插入区间端点(0或i-1),折点数量+1。

  • 所以f[i][j]=f[i−1][j]∗x+f[i−1][j−1]∗y+f[i−1][j−2]∗z

x,y,z分别为放弃后折点数量+0,+1,+2的方案数。
所以f[i][j]=f[i−1][j]∗j+f[i−1][j−1]∗2+f[i−1][j−2]∗(i−j).

#include<bits/stdc++.h>
using namespace std;
const int mod = 123456;
int f[550][550];

int main(){
    int n, k;  cin>>n>>k;
    f[1][1] = 1;  f[2][1] = 2;
    for(int i = 3; i <= n; i++){
        for(int j = 1; j<=k && j<=i; j++){
            f[i][j] = (f[i][j]+f[i-1][j]*j%mod+f[i-1][j-1]*2%mod)%mod;
            if(j>1)f[i][j] = (f[i][j]+f[i-1][j-2]*(i-j))%mod;
        }
    }
    cout<<f[n][k]<<"\n";
    return 0;
}

第8题——解谜游戏 (20分)

  • 题意:
  • 思路:多组样例,每次三个字符串,可以旋转或者交换内外圈0点,求能否外圈绿色,中圈红色,内圈黄色。
  • 模拟转圈的过程,可以发现,内圈转完4次(转完一圈),中圈转半圈,大圈转三分之一圈,那么可以知道,内圈0号位置对应中圈有两个位置(0号和4号),外圈有三个位置(0号、4号、8号,即可以发生换圈位置的情况)。
  • 可以分成四个组,只有在组内才能发生跨圈的操作,所以只要每个的条件都满足1个Y、2个R、3个G就可以达成目标输出YES。

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        int T;  cin>>T;
        while(T--){
            string a, b, c;  cin>>a>>b>>c; //外,中,内
            int z[200] = {0}, ok = 1;
            for(int i = 0; i < 4; i++){ //四组
                z[c[(i+4)%4]-'A']++;//内
                z[b[(i+4)%4]-'A']++;//中
                z[b[(i+4)%8]-'A']++;
                z[a[(i+4)%4]-'A']++;//外
                z[a[(i+4)%12]-'A']++;
                z[a[(i+8)%12]-'A']++;
                if(z['R'-'A']==2 && z['G'-'A']==3 && z['Y'-'A']==1){
                    memset(z,0,sizeof(z));
                }else{
                    // cout<<z['R'-'A']<<" "<<z['G'-'A']<<"\n";
                    ok = 0;  break;
                }
            }
            if(ok==1)cout<<"YES\n";
            else cout<<"NO\n";
        }
        return 0;
    }
    
    

第9题——第八大奇迹 (25分)

  • 题意:

【样例输入】
10 15
C 1 10
C 2 20
C 3 30
C 4 40
C 5 50
C 6 60
C 7 70
C 8 80
C 9 90
C 10 100
Q 1 2
Q 1 10
Q 1 8
C 10 1
Q 1 10
【样例输出】
0
30
10
20

  • 思路:

1-1e5的数组,初始为0,单点修改,查询区间第8大的值。
emmm,众所周知的动态区间第k小问题。
静态主席树:可持久化权值线段树。×
动态主席树:树套树。√

  • 主席树或者线段树维护10个节点(区间最大的8个值,每次更新从左右子树排序过的最大的8个值pushup)吧,虽然板子不好敲,但还是挺板的

所以我选线段树()


```cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int val[maxn<<2][8], sum[maxn<<2];//区间长度

//线段树
#define lch (p<<1)
#define rch (p<<1|1)
void pushup(int p){
    //按从大到小合并左右区间到当前区间(类似于归并)
    int a = 0, b = 0, cc = 0;
    while(a < sum[lch] && b < sum[rch] && cc < 8){
        if(val[lch][a] > val[rch][b]){
            val[p][cc++] = val[lch][a++];
        }else{
            val[p][cc++] = val[rch][b++];
        }
    }
    while(a<sum[lch] && cc < 8)val[p][cc++] = val[lch][a++];
    while(b<sum[rch] && cc < 8)val[p][cc++] = val[rch][b++];
    sum[p] = min(8, sum[lch]+sum[rch]);
}
void add(int p, int l, int r, int x, int v){
    if(l==r){
        val[p][0] = v;
        sum[p] = 1;
        return ;
    }
    int mid = l+r>>1;
    if(x <= mid){
        add(lch, l, mid, x, v);
    }else{
        add(rch, mid+1, r, x, v);
    }
    pushup(p);
}
int tmp[100];
void merge(int kk[], int k1[], int k2[]){
    int a = 0, b = 0, cc = 0;
    while(a < 8 && b < 8){
        if(k1[a]>=k2[b])tmp[cc++]=k1[a++];
        else tmp[cc++]=k2[b++];
    }
    while(a<8)tmp[cc++]=k1[a++];
    while(b<8)tmp[cc++]=k2[b++];
    for(int i = 0; i < 8; i++)kk[i]=tmp[i];
}
int* query(int p, int l, int r, int ql, int qr){
    if(ql<=l && r<=qr)return val[p];
    int mid = l+r>>1;
    if(qr <= mid)return query(lch, l, mid, ql, qr);
    else if(ql>mid)return query(rch,  mid+1, r, ql, qr);
    int* k1 = query(lch, l, mid, ql, qr);
    int* k2 = query(rch,  mid+1, r, ql, qr);
    int* kk = new int[9];
    merge(kk, k1, k2);
    return kk;
}

int main(){
    int n, m;  cin>>n>>m;
    for(int i = 1; i <= m; i++){
        string op;  cin>>op;
        if(op=="C"){
            int p, x;  cin>>p>>x;
            add(1,1,n,p,x);
        }else{
            int a, b;  cin>>a>>b;
            cout<<query(1,1,n,a,b)[7]<<"\n";
        }
    }
    return 0;
}


```



第10题——燃烧权杖 (25分)

  • 题意:

2个英雄,k个士兵,k+2个人都有血槽。
随机选择一个人扣10点血,直到出现死英雄为止。
求死的英雄是敌方的概率。
输出答案%p

  • 思路:

因为只跟英雄有关系,所以可以忽视小兵的存在。
变成了每次自己或地方英雄随机扣血10滴谁先挂,满足二项分布。

  • 留着占坑待填吧,暂时不会了。

50分做法可以参考一下别的大佬的题解,但是下午和明天都有期末考试的我已经不想看了

目录
相关文章
|
4月前
|
负载均衡 算法 安全
探秘:基于 C++ 的局域网电脑控制软件自适应指令分发算法
在现代企业信息化架构中,局域网电脑控制软件如同“指挥官”,通过自适应指令分发算法动态调整指令发送节奏与数据量,确保不同性能的终端设备高效运行。基于C++语言,利用套接字实现稳定连接和线程同步管理,结合实时状态反馈,优化指令分发策略,提升整体管控效率,保障网络稳定,助力数字化办公。
104 19
|
5月前
|
算法 安全 C++
用 C++ 算法控制员工上网的软件,关键逻辑是啥?来深度解读下
在企业信息化管理中,控制员工上网的软件成为保障网络秩序与提升办公效率的关键工具。该软件基于C++语言,融合红黑树、令牌桶和滑动窗口等算法,实现网址精准过滤、流量均衡分配及异常连接监测。通过高效的数据结构与算法设计,确保企业网络资源优化配置与安全防护升级,同时尊重员工权益,助力企业数字化发展。
101 4
|
11月前
|
存储 机器学习/深度学习 算法
第十五届蓝桥杯pb组国赛E题[马与象] (15分)BFS算法 详解
第十五届蓝桥杯pb组国赛E题[马与象] (15分)BFS算法 详解
211 3
|
7月前
|
算法 C++
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
188 5
|
9月前
|
安全 网络安全 数据安全/隐私保护
探索企业上网行为管理软件:C++ 的视角
在数字化企业环境中,上网行为管理软件至关重要,它不仅保障信息安全还优化网络资源分配。C++以高效和强大性能为基础,支持这类软件的开发。通过示例代码展示了如何使用C++捕获网络数据包、控制特定网址访问及分析网络流量模式,展现了C++在处理大规模网络数据方面的优势,满足企业对网络安全与管理的需求。
74 1
|
11月前
|
存储 算法 测试技术
第十五届蓝桥杯大赛 国赛 pb组F题【括号与字母】(15分) 栈的应用
第十五届蓝桥杯大赛 国赛 pb组F题【括号与字母】(15分) 栈的应用
100 1
|
11月前
|
Java
2023届蓝桥杯大赛软件类国赛Java大学B组 互质 数论
2023届蓝桥杯大赛软件类国赛Java大学B组 互质 数论
87 1
|
11月前
|
存储 前端开发 算法
2016届蓝桥杯大赛软件类国赛Java大学B组 反幻方 暴力搜索
2016届蓝桥杯大赛软件类国赛Java大学B组 反幻方 暴力搜索
72 0
|
人工智能 算法 Java
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1005 数字游戏
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1005 数字游戏
156 0
|
Java C语言 C++
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1000 kAc给糖果你吃
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1000 kAc给糖果你吃
119 0