当前位置: 首页 > news >正文

蓝桥杯第十五届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)//防止重复在开方数后插入tempa[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)//防止重复在开方数后插入tempa[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; // 如果没有找到路径,输出-1elsecout << 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;
}

http://www.mrgr.cn/news/97839.html

相关文章:

  • PDFBox/Itext5渲染生成pdf文档
  • React 响应事件
  • skynet.dispatch 使用详解
  • js 拷贝-包含处理循环引用问题
  • React十案例下
  • 如何在idea中快速搭建一个Spring Boot项目?
  • LeetCode Hot100 刷题笔记(8)—— 动态规划(一维、二维)
  • 前端三件套—CSS入门
  • WebGL数学手记:矩阵基础
  • React八案例上
  • python爬虫:DrissionPage实战教程
  • 柑橘病虫害图像分类数据集OrangeFruitDataset-8600
  • STM32——I2C通讯(软件模拟)
  • 探秘 Svelte+Vite+TS+Melt - UI 框架搭建,开启高效开发
  • PostgreSQL-数据库的索引 pg_operator_oid_index 损坏
  • SpringBoot 整合 MCP
  • JS 箭头函数
  • 【设计模式】面向对象开发学习OOPC
  • Apache Nifi安装与尝试
  • 研究嵌入式软件架构时遇到的初始化堆栈溢出问题