第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

我模拟了一下, 分享一下我的做题感受和经验, 希望能对你有用

这个比赛的时长是四个小时, 初赛的目标是拿到省一, 才有机会进入国赛

我也不知道, 这个分能不能进国赛, 算了,不管了, 直接开讲, 我本人一般刷面试题比较多, 这种比赛题平常基本不怎么写

1. 握手问题

题目链接: 蓝桥账户中心

题目如下:

问题描述

小蓝组织了一场算法交流会议,总共有 50 人参加了本次会议。在会议上,大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 7 个人,这 7 人彼此之间没有进行握手 (但这 7 人与除这 7 人以外的所有人进行了握手)。请问这些人之间一共进行了多少次握手?

注意 A 和 B 握手的同时也意味着 B 和 A 握手了,所以算作是一次握手。

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
  int a=50; int b=7;
  int res=0;
  int totalNum=a*(a-1)/2;
  totalNum-=(b*(b-1)/2);
  cout<<totalNum<<endl;
  return 0;
}

题意就是说, 有43个人可以和其他人两两握手, 剩下的7个人, 内部不能两两握手, 但是能和其他的43个人握手, 问这些人一共进行握了多少次手

这其实是个数学题, 比赛的时候直接用计算器算一下就行, 不用写代码

思路1:分两类 43个人分一组记作A组, 7个人分一组记作B组, 43个内部随便握手,A组:43*42/2=903(解释一下为啥要/2, 类似于均摊法, 图论中中每条边连接着两个节点, 在计算度数和时, 每条边会被重复计算两次, 结果要除2, 这里握手就相当于两个人(节点)连接了一条边,结果也要/2), B组:7*43=301,最后结果就是301+903=1204次

思路2:整体-特殊(就是我代码中写的)

50个人两两握手减去7个人两两握手

50*49/2-7*6/2=1204

2. 小球反弹

有一长方形,长为 343720343720 单位长度,宽为 233333233333 单位长度。在其内部左上角顶点有一小球 (无视其体积),其初速度如图所示且保持运动速率不变,分解到长宽两个方向上的速率之比为 dx:dy=15:17。小球碰到长方形的边框时会发生反弹,每次反弹的入射角与反射角相等,因此小球会改变方向且保持速率不变(如果小球刚好射向角落,则按入射方向原路返回)。从小球出发到其第一次回到左上角顶点这段时间里,小球运动的路程为多少单位长度?答案四舍五入保留两位小数。

这是道物理题, 如果你是初中生, 就直接秒了, 但我模拟比赛的时候却被秒了

题意:从做左上角按入射角tan &=dx/dy=15/17射入一条光线, 在矩形中进行反射,求重新返回左上角所经过的路程。

解法:按我下面, 图中的画的进行对称补偿就行

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int main(){
    long long times=1,x=343720,y=233333;
    while(true){
    if((15*times)%x==0&&(17*times)%y==0) break;
        times++;
    }
    cout << fixed << setprecision(2) << 2*sqrt(15*15*times*times+17*17*times*times) << endl;
    return 0;
}

3. 好数

题目如下:

问题描述

一个整数如果按从低位到高位的顺序,奇数位 (个位、百位、万位 ⋯⋯ ) 上的数字是奇数,偶数位 (十位、千位、十万位 ⋯⋯ ) 上的数字是偶数,我们就称之为 "好数"。

给定一个正整数 N,请计算从 1 到 N 一共有多少个好数。

输入格式

一个整数 N。

输出格式

一个整数代表答案。
解题思路:按题目进行模拟就行(计数还是不计数,删除还是不删除)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
bool isGoodNum(int num) {
    int pos = 1;
    while (num > 0) {
        int digit = num % 10;
        if (pos % 2 == 1) { 
            if (digit % 2 == 0) {
                return false;
            }
        } else { 
            if (digit % 2 == 1) { 
                return false;
            }
        }
        num /= 10;
        pos++;
    }
    return true;
}

int main() {
    int N;
    cin >> N;
    int res = 0;
    for (int i = 1; i <= N; i++) { 
        if (isGoodNum(i)) {
            res++;
        }
    }
    cout << res << endl;
    return 0;
}
------------------------
#include <bits/stdc++.h>
using namespace std;
bool isGoodNum(string num) {
    int pos = 1; int n=num.size();
    while (num.size() > 0) {
        int digit = num[num.size()-1]-'0';
        if(pos%2==1&&digit%2==1){
            pos++;  num.erase(num.size()-1); continue; 
        }else if(pos%2==0&&digit%2==0){
            pos++;  num.erase(num.size()-1); continue;
        }else{
            break;
        }
    }
    if(pos-1==n){
        return true;
    }
    return false;
}

int main() {
    int N;
    cin >> N;
    int res = 0;
    for (int i = 1; i <= N; i++) {
        string s=to_string(i);
        if (isGoodNum(s)) {
            res++;
        }
    }
    cout << res << endl;
    return 0;
}
------------------------
#include <bits/stdc++.h>
using namespace std;
bool isGoodNum(string num) {
    int n = num.size();
    for (int i = n - 1; i >= 0; i--) {
        int digit = num[i] - '0';
        int pos = n - i; 
        if ((pos % 2 == 1 && digit % 2 == 0) || (pos % 2 == 0 && digit % 2 == 1)) {
            return false;
        }
    }
    return true;
}

int main() {
    int N;
    cin >> N;
    int res = 0;
    for (int i = 1; i <= N; i++) {
        string s = to_string(i);
        if (isGoodNum(s)) {
            res++;
        }
    }
    cout << res << endl;
    return 0;
}
-------------------------
#include <bits/stdc++.h>
using namespace std;

bool isGoodNum(string num) {
    int pos = 1;
    int n = num.size();
    while (!num.empty()) {
        int digit = num.back() - '0';
        if ((pos % 2 == 1 && digit % 2 == 1) || (pos % 2 == 0 && digit % 2 == 0)) {
            pos++;
            num.pop_back();
        } else {
            return false;
        }
    }
    return true;
}

int main() {
    int N;
    cin >> N;
    int res = 0;
    for (int i = 1; i <= N; i++) {
        string s = to_string(i);
        if (isGoodNum(s)) {
            res++;
        }
    }
    cout << res << endl;
    return 0;
}

4. R格式

题目如下:

问题描述

小蓝最近在研究一种浮点数的表示方法:RR 格式。对于一个大于 0 的浮点数 d,可以用 RR 格式的整数来表示。给定一个转换参数 n,将浮点数转换为 R 格式整数的做法是:

  1. 将浮点数乘以 2^n;

  2. 四舍五入到最接近的整数。

输入格式

一行输入一个整数 n 和一个浮点数 d,分别表示转换参数,和待转换的浮点数。

输出格式

输出一行表示答案:d 用 R 格式表示出来的值。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    double d;
    cin >> n >> d;
    double p = pow(2.0, n);
    double res = d * p;
    int r = round(res);
    cout << r << endl;
    return 0;
}
这是模拟比赛时的代码, 提交后发现只过了40%

思路:面试题中很少有卡输入输出精度的情况,我又被秒了

正确的解法如下:把数字转换成字符串进行处理,我的建议是你对下面小数进位乘法的板子熟悉的话,就一步一步写。如果你只是想过掉尽量多的用例, 不追求满分的话,就在第一段代码中把数据范围开的尽量大一些,int r->long long r, 能过掉50%,也能拿到10分

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
string solve(string& num){
  string res;
  int c=0;
  for(int j=num.size()-1;j>=0;j--){
    int d=num[j]-'0';
    int p=d*2+c;
    c=p/10;
    res.push_back((p%10)+'0');
  }
  if(c>0){
    res.push_back(c+'0');
  }
  reverse(res.begin(),res.end());
  return res;
}
string addOne(string num){
  int c=1;
  for(int i=num.size()-1;i>=0;i--){
    int d=num[i]-'0';
    int sum=d+c;
    num[i]=(sum%10)+'0';
    c=sum/10;
  }
  if(c>0){
    num.insert(num.begin(),'1');
  }
  return num;
}
int main(){
  int n; string d;
  cin>>n>>d;
  int point=d.find('.');
  string frontNum=d.substr(0,point);
  string backNum=d.substr(point+1);
  int k=backNum.size();
  while(k>0&&backNum[k-1]=='0') k--;
  backNum=backNum.substr(0,k);
  string Num=frontNum+backNum;
  for(int i=0;i<n;i++){
      Num=solve(Num);
  }
  int m=Num.size();
  string q,r;
  if(m<=k){
      q="0";
      r=Num+string(k-m,'0');
  }else{
      q=Num.substr(0,m-k);
      r=Num.substr(m-k);
  }
  string h(k,'0');
  h[0]='5';
  if(r>=h){
    q=addOne(q);
  }
  cout<<q<<endl;
  return 0;
}

5. 宝石组合

题目链接:

蓝桥账户中心

解题思路:根据题目中给出的公式很容易推导出s=gcd(a,b,c), 所以题目就转换成求gcd(a,b,c) 的最大值

代码如下:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int freq[100001]={0};
int main(){
    int N;
    cin>>N;
    vector<int> a(N);
    for(int i=0;i<N;i++){
      cin>>a[i];
      freq[a[i]]++;
    }
    int maxA=*max_element(a.begin(),a.end());
    vector<int> cnt(maxA+1,0);
    for(int d=1;d<=maxA;d++){
      for(int k=d;k<=maxA;k+=d){
        cnt[d]+=freq[k];
      }
    }
    int bestD=-1;
    for(int d=maxA;d>=1;d--){
      if(cnt[d]>=3){
        bestD=d;
        break;
      }
    }
    vector<int> c;
    for(int x: a){
      if(x%bestD==0){
        c.push_back(x);
      }
    }
    sort(c.begin(),c.end());
    cout << c[0] << " " << c[1] << " " << c[2] << endl;
    return 0;
}

6. 数字接龙

题目链接:

https://www.lanqiao.cn/problems/19712/learning/

输出从(0,0)到(N-1,N-1)的最小字典序路径, 思路就是dfs/回溯

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
int N, K;
vector<vector<int>> grid;
vector<vector<bool>> visited;
string result = "";
bool isValid(int x, int y) {
    return x >= 0 && x < N && y >= 0 && y < N;
}
bool solve(pair<int, int> a1, pair<int, int> a2, pair<int, int> b1, pair<int, int> b2) {
    double x1 = a1.first, y1 = a1.second;
    double x2 = a2.first, y2 = a2.second;
    double x3 = b1.first, y3 = b1.second;
    double x4 = b2.first, y4 = b2.second;
    double den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
    if (den == 0) return false; 

    double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den;
    double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den;

    if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
        double px = x1 + t * (x2 - x1);
        double py = y1 + t * (y2 - y1);
        bool isAStart = (px == x1 && py == y1);
        bool isAEnd = (px == x2 && py == y2);
        bool isBStart = (px == x3 && py == y3);
        bool isBEnd = (px == x4 && py == y4);
        if (!isAStart && !isAEnd && !isBStart && !isBEnd) {
            return true;
        }
    }
    return false;
}

void dfs(int x, int y, int num, int count, string path, vector<pair<pair<int, int>, pair<int, int>>>& segments) {
    if (x == N-1 && y == N-1) {
        if (count == N * N) {
            if (result.empty() || path < result) {
                result = path;
            }
        }
        return;
    }

    for (int i = 0; i < 8; i++) { 
        int nx = x + dx[i];
        int ny = y + dy[i];
        if (isValid(nx, ny) && !visited[nx][ny] && grid[nx][ny] == (num + 1) % K) {
            pair<int, int> current(x, y), next(nx, ny);
            bool cross = false;
            for (auto& seg : segments) {
                if (solve(current, next, seg.first, seg.second)) {
                    cross = true;
                    break;
                }
            }
            if (!cross) {
                visited[nx][ny] = true;
                segments.emplace_back(current, next);  //记录走过的线段
                dfs(nx, ny, (num + 1) % K, count + 1, path + to_string(i), segments);
                segments.pop_back();
                visited[nx][ny] = false;
            }
        }
    }
}
int main() {
    cin >> N >> K;
    grid.resize(N, vector<int>(N));
    visited.resize(N, vector<bool>(N, false));

    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < N; ++j) {
            cin >> grid[i][j];
        }
    }
    if (grid[0][0] != 0) { 
        cout << -1 << endl;
        return 0;
    }

    visited[0][0] = true;
    vector<pair<pair<int, int>, pair<int, int>>> segments;
    dfs(0, 0, 0, 1, "", segments);

    if (result.empty()) {
        cout << -1 << endl;
    } else {
        cout << result << endl;
    }

    return 0;
}

7. 拔河

问题描述

小明是学校里的一名老师,他带的班级共有 n 名同学,第 i 名同学力量值为 ai​。在闲暇之余,小明决定在班级里组织一场拔河比赛。

为了保证比赛的双方实力尽可能相近,需要在这 nn 名同学中挑选出两个队伍,队伍内的同学编号连续:{al1,al1+1,...,ar1−1,ar1} 和 {al2,al2+1,...,ar2−1,ar2,其中l1​≤r1​<l2​≤r2​。

两个队伍的人数不必相同,但是需要让队伍内的同学们的力量值之和尽可能相近。请计算出力量值之和差距最小的挑选队伍的方式。
解题思路: 我模拟赛时, 用前缀和按顺序模拟题意, 四层循环, 含泪只过了三个样例...

代码如下

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    vector<int> prefix(n + 1, 0); 
    for (int i = 0; i < n; ++i) {
        prefix[i + 1] = prefix[i] + a[i];
    }
    int minDiff = INT_MAX;
    for (int l1 = 0; l1 < n; l1++) {
        for (int r1 = l1 + 1; r1 <= n; r1++) {
            int sum1 = prefix[r1] - prefix[l1];
            for (int l2 = r1; l2 < n; l2++) {
                for (int r2 = l2 + 1; r2 <= n; r2++) {
                    int sum2 = prefix[r2] - prefix[l2];
                    minDiff = min(minDiff, abs(sum1 - sum2));
                }
            }
        }
    }
    cout << minDiff << endl;
    return 0;
}

正确解法的时间复杂度为O(n^2*logn), 只要你写的代码时间复杂度<O(n^3)就能过

要不枚举左子数组的结束位置, 要不枚举右子数组的开始位置, 分割点划分给左/右子数组都可以

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cin >> n;
    vector<long long> a(n + 1);
    vector<long long> prefix(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        prefix[i] = prefix[i - 1] + a[i];
    }

    long long ans = LLONG_MAX;
    set<long long> sumBSet;
    for (int rA = n; rA >= 1; rA--) {
        int lB = rA + 1;
        if (lB <= n) {
            for (int rB = lB; rB <= n; rB++) {
                long long sumB = prefix[rB] - prefix[rA];
                sumBSet.insert(sumB);
            }
        }
        for (int l1 = 1; l1 <= rA; l1++) {
            long long sumA = prefix[rA] - prefix[l1 - 1];
            auto it = sumBSet.lower_bound(sumA);
            if (it != sumBSet.end()) {
                ans = min(ans, abs(sumA - *it));
            }
            if (it != sumBSet.begin()) {
                ans = min(ans, abs(sumA - *prev(it))); //前后对比
            }
            if (ans == 0) {
                cout << 0 << endl;
                return 0;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

总结:题目有点偏数学, 没考dp, 常见的题像搜索, 滑窗, 前缀和, 差分, 双指针, 左右维护, 还有简单一点的贪心, 你都要搞懂, 最后再写几道数学题, 基本上就差不多了, 比赛的时候, 如果没有双机位, 可以和旁边的同学适当讨论一下, 感觉有一些数学技巧确实有点难想。如果你很强,就当我没说。有什么疑问可以发到评论区,感谢!!!

相关推荐
YuforiaCode21 分钟前
第十六届蓝桥杯 2025 C/C++组 密密摆放
c语言·c++·蓝桥杯
潇-xiao1 小时前
Qt实现 hello world + 内存泄漏(5)
c++·qt
智驾2 小时前
C++,设计模式,【建造者模式】
c++·设计模式·建造者模式
Invinciblenuonuo2 小时前
实习技能记录【4】-----消息分发中的观察者模型
c++
Wabi_sabi_x3 小时前
C++设计模式:面向对象的八大设计原则之三
开发语言·c++·设计模式
qq_447429413 小时前
数据结构与算法:图论——最短路径
c语言·数据结构·c++·图论
yi个名字3 小时前
C++ STL vector容器详解:从原理到实践
开发语言·c++
xin007hoyo4 小时前
算法笔记.求约数
c++·笔记·算法
共享家95274 小时前
C++ 多态:原理、实现与应用
c++
虾球xz4 小时前
c++环境和vscode常用的一些有用插件
c++·ide·vscode