蓝桥杯第十五届C++B组省赛真题解析

蓝桥杯第十五届C++B组省赛真题解析


一、宝石组合https://www.lanqiao.cn/problems/19711/learning/

解题思路

题目要求找到三个数,使得它们的最大公约数(GCD)尽可能大,并在GCD相同的情况下选择数值最小的三个数。以下是分步解析:

  1. ​公式化简​​:

    • 通过数学推导,将三个数的最小公倍数(LCM)转化为涉及GCD的表达式:
      LCM ( a , b , c ) = a × b × c × GCD ( a , b , c ) GCD ( a , b ) × GCD ( b , c ) × GCD ( a , c ) \text{LCM}(a, b, c) = \frac{a \times b \times c \times \text{GCD}(a, b, c)}{\text{GCD}(a, b) \times \text{GCD}(b, c) \times \text{GCD}(a, c)} LCM(a,b,c)=GCD(a,b)×GCD(b,c)×GCD(a,c)a×b×c×GCD(a,b,c)
    • 题目中的目标函数 $ S $ 最终简化为三个数的最大公约数:
      S = GCD ( a , b , c ) S = \text{GCD}(a, b, c) S=GCD(a,b,c)
  2. ​预处理因数​​:

    • 对每个输入的数,计算其所有因数,并将该数记录到对应因数的列表中。
    • ​例如​​:数 12 的因数为 1, 2, 3, 4, 6, 12 ,每个因数的列表都会添加12。
  3. ​搜索最大GCD​​:

    • ​从大到小遍历所有可能的因数​​ d ,检查对应列表是否包含至少三个数。
    • 第一个满足条件的 $ d $ 即为最大可能的GCD,因为更大的因数已被遍历过且不满足条件。
  4. ​选择最小字典序​​:

    • 对满足条件的列表排序,取前三个最小的数,确保在相同GCD时结果字典序最小。

公式推导细节

  1. ​三数LCM的展开​​:
    LCM ( a , b , c ) = a ⋅ b ⋅ c GCD ( a , b ) ⋅ GCD ( b , c ) ⋅ GCD ( a , c ) ⋅ GCD ( a , b , c ) \text{LCM}(a, b, c) = \frac{a \cdot b \cdot c}{\text{GCD}(a, b) \cdot \text{GCD}(b, c) \cdot \text{GCD}(a, c)} \cdot \text{GCD}(a, b, c) LCM(a,b,c)=GCD(a,b)GCD(b,c)GCD(a,c)abcGCD(a,b,c)

  2. ​目标函数 $ S $ 的化简​​:
    S = LCM ( a , b , c ) ⋅ GCD ( a , b ) ⋅ GCD ( b , c ) ⋅ GCD ( a , c ) a ⋅ b ⋅ c = GCD ( a , b , c ) S = \frac{\text{LCM}(a, b, c) \cdot \text{GCD}(a, b) \cdot \text{GCD}(b, c) \cdot \text{GCD}(a, c)}{a \cdot b \cdot c} = \text{GCD}(a, b, c) S=abcLCM(a,b,c)GCD(a,b)GCD(b,c)GCD(a,c)=GCD(a,b,c)


算法步骤

  1. ​预处理​​:

    • 遍历每个数,分解其所有因数,将数存入对应因数的容器中。
    • ​时间复杂度​​:$ O(n \sqrt{\text{max_value}}) $,其中 $ n $ 是输入数的个数。
  2. ​枚举最大GCD​​:

    • 从最大可能的因数(如 $ 10^5 $)开始倒序遍历,找到第一个满足条件的因数。
    • ​时间复杂度​​:$ O(\text{max_value}) $。
  3. ​输出结果​​:

    • 对符合条件的三数排序,选择字典序最小的组合。
    • ​时间复杂度​​:$ O(k \log k) $,其中 $ k $ 是容器中数的数量。

#include <bits/stdc++.h>
using namespace std;
vector<int> a[100010];
int main()
{
  int n;
  cin >> n;
  for(int i=0; i<n; i++){
    int temp;
    cin >> temp;

    for(int j=1; j*j<=temp; j++){
      //找到temp的所有因数,并将temp插入到每个因数的后面
       if(temp%j==0){
      
         a[j].push_back(temp);
         if(j!=temp/j)//防止重复在开方数后插入temp
         a[temp/j].push_back(temp);
       }
    }
  }
  for(int i=100000;i>=0; i--){
    if(a[i].size()>=3){
      sort(a[i].begin(),a[i].end());
      cout << a[i][0]<< " "<<  a[i][1]<< " " << a[i][2];
      return 0;
    }
  }
  return 0;
}

二、拔河

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

问题描述

给定一个表示同学力量值的数组,要求找到​​两个不同子数组和的最小差值​​。允许子数组重叠甚至完全重合(这意味着差值为0是可能的)。


算法思路

1. 前缀和计算
  • ​目标​​:快速计算任意子数组的和,避免双重循环累加。
  • ​方法​​:构建前缀和数组 pre[],其中 pre[i] 表示原数组前 i 项的和。
    p r e [ i ] = { a [ 0 ] if  i = 0 p r e [ i − 1 ] + a [ i ] if  i > 0 pre[i] = \begin{cases} a[0] & \text{if } i = 0 \\ pre[i-1] + a[i] & \text{if } i > 0 \end{cases} pre[i]={a[0]pre[i1]+a[i]if i=0if i>0
2. 生成所有子数组和
  • ​步骤​​:
    1. 通过双重循环枚举所有可能的子数组起止点 ij
    2. 使用前缀和公式计算区间 [i, j] 的和:
      sum ( i , j ) = { p r e [ j ] if  i = 0 p r e [ j ] − p r e [ i − 1 ] if  i > 0 \text{sum}(i, j) = \begin{cases} pre[j] & \text{if } i = 0 \\ pre[j] - pre[i-1] & \text{if } i > 0 \end{cases} sum(i,j)={pre[j]pre[j]pre[i1]if i=0if i>0
    3. 将所有子数组和存入集合 s 中。
3. 排序优化查找
  • ​关键性质​​:排序后的数组中,最小差值必然出现在相邻元素之间。
  • ​步骤​​:
    1. 对集合 s 排序。
    2. 遍历排序后的数组,计算相邻元素的差值,记录最小值。

时间复杂度

步骤时间复杂度
前缀和计算O(n)
生成所有子数组和O(n^2)
排序子数组和集合O(n^2 \log n)

​适用场景​​:当 $ n \leq 1000 $ 时,算法效率可接受。


代码实现

#include <bits/stdc++.h>
using namespace std;
vector<int> a[100010];
int main()
{
  int n;
  cin >> n;
  for(int i=0; i<n; i++){
    int temp;
    cin >> temp;

    for(int j=1; j*j<=temp; j++){
      //找到temp的所有因数,并将temp插入到每个因数的后面
       if(temp%j==0){
      
         a[j].push_back(temp);
         if(j!=temp/j)//防止重复在开方数后插入temp
         a[temp/j].push_back(temp);
       }
    }

  }
  for(int i=100000;i>=0; i--){
    if(a[i].size()>=3){
      sort(a[i].begin(),a[i].end());
      cout << a[i][0]<< " "<<  a[i][1]<< " " << a[i][2];
      return 0;
    }
  }
  return 0;
}


三、数字接龙

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


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

const int N = 11; // 定义棋盘的最大大小为11×11
int n, k; // n为棋盘大小,k为数字循环的范围
int g[N][N]; // 存储棋盘上的数字
int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; // 定义8个方向的x坐标偏移
int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1}; // 定义8个方向的y坐标偏移
string path; // 存储路径的方向编号
bool st[N][N]; // 标记棋盘上的格子是否被访问过
bool edge[N][N][N][N]; // 检查路径是否交叉

// 深度优先搜索函数,用于寻找路径
bool dfs(int a, int b) {
    // 如果到达右下角格子,检查路径长度是否为n*n-1(因为起点不计入路径)
    if (a == n - 1 && b == n - 1)
        return path.size() == n * n - 1;

    st[a][b] = true; // 标记当前格子已访问
    for (int i = 0; i < 8; i++) { // 遍历8个方向
        int x = a + dx[i], y = b + dy[i]; // 计算目标格子的坐标
        // 检查目标格子是否越界、是否访问过、数字是否满足循环序列要求
        if (x < 0 || x >= n || y < 0 || y >= n) continue;
        if (st[x][y]) continue;
        if (g[x][y] != (g[a][b] + 1) % k) continue;
        // 检查路径是否交叉(对于斜向移动,检查是否有反向的路径)
        if (i % 2 && (edge[a][y][x][b] || edge[x][b][a][y])) continue;

        edge[a][b][x][y] = true; // 标记路径
        path += i + '0'; // 将方向编号加入路径
        if (dfs(x, y)) return true; // 递归搜索下一个格子
        path.pop_back(); // 回溯,移除路径中的最后一个方向
        edge[a][b][x][y] = false; // 回溯,取消路径标记
    }
    st[a][b] = false; // 回溯,取消当前格子的访问标记
    return false; // 如果所有方向都无法到达终点,返回false
}

int main() {
    cin >> n >> k; // 输入棋盘大小和数字循环范围
    for (int i = 0; i < n; i++) // 读取棋盘上的数字
        for (int j = 0; j < n; j++)
            cin >> g[i][j];

    // 从起点(0,0)开始搜索路径
    if (!dfs(0, 0))
        cout << -1 << endl; // 如果没有找到路径,输出-1
    else
        cout << path << endl; // 输出路径的方向编号序列

    return 0;
}

---

四、小球反弹

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

#include <iostream>
#include <math.h>
using namespace std;
int main()
{
  int  x=343720,y=233333;
  long long t=1;

  while(1){
    if(15*t % x == 0 && 17*t%y == 0) break;
    t++;//当小球弹到长方形的某个角后,将原路返回;
  }
  long long t1 = t*t*15*15+t*t*17*17;
  
  printf("%.2f",2*sqrt(t1));
  return 0;
}
### 第十五届蓝桥杯C++ A信息 #### 比题目获取 为了获得第十五届蓝桥杯C++ A的具体题目,建议访问官方发布的资源库。对于研究生而言,可以参考已有的公开资源文件来推测可能的题型和难度水平[^1]。 #### 参规则概述 通常情况下,蓝桥杯有着严格的参规定,包括但不限于身份验证、考试纪律以及编程环境的要求。具体到A,虽然没有直接提及A的信息,但从其他别的描述中可以看出一些共通之处。例如,确保熟悉考场设备是非常重要的;有参者提到使用的键盘状况不佳影响发挥,因此提前适应场地条件有助于减少不必要的干扰[^3]。 #### 准备资料推荐 针对准备阶段,可以从多个方面入手收集学习材料: - **历年真题解析**:通过研究往年的试题及其解答方案加深理解。 - **算法训练平台**:利用在线OJ(Online Judge)系统强化实践能力。 - **技术文档与书籍**:查阅权威的技术手册或指南书目补充理论基础。 此外,还可以借鉴他人的备考经验和心得笔记作为辅助参考资料。一位参与者分享了其关于C++大学C的心得体会,并提供了个人整理的部分题解思路供后来者参考[^2]。 ```cpp // 示例代码片段用于展示如何高效地解决某一类常见问题 #include <iostream> using namespace std; int main() { int n; cin >> n; // 输入数据量大小n vector<int> data(n); for (auto &item : data){ cin >> item; // 接收输入序列 } sort(data.begin(), data.end()); // 对数进行排序操作 cout << "Sorted array is:" << endl; for(auto num:data){ cout<<num<<" "; // 输出处理后的结果 } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值