试题A------握手问题

一、解题思路
直接用高中学的排列组合思路

二、代码示例
cpp
#include<bits/stdc++.h>
using namespace std;
int fun(int n)
{
int sum=0;
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)sum++;
}
return sum;
}
int main()
{
cout<<fun(50)-fun(7);
}
三、感悟:
这是一个签到题,都不需要编程都可以做出来
试题B------小球反弹

一、解题思路
大家可以先思考一下,因为题目要求是从左上角射出到第二次到达左上角,在x方向上,是不是一定走了偶数个343720长度,在y方向上,是不是一定也走了偶数个233333长度,设走了i个343720长度,j个233333长度,那x方向上是不是一共走了343720*i长度,y方向上一共走了233333*j长度,斜率又已知,那x方向走的长度比上y方向走的长度是不是就是dx/dy,遍历i,j,就可以求出来了
如果还是不太理解可以看一下下图:

假设小球经过四次撞击就可以回到原点(因为我是对称画的,实际上没有右边这一部分,但是撞击后走的路程是相同的,因为斜率是一定的,路程一样,那在x和y上面走的路程其实也是相同的)所以它最后在x上面走的路程和y上面走的路程是成一定比例的。
二、代码示例
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int main()
{
//因为i,j一定是偶数,每次加2就行了
for(ll i=2;i<=10000;i+=2)
{
for(ll j=2;j<=10000;j+=2)
{
if(i*343720*17==j*233333*15)
{
cout<<i<<" "<<j<<endl;
printf("%.2lf",sqrt((i*343720)*(i*343720)+(j*233333)*(j*233333)));
return 0;
}
}
}
}
三、感悟
这题我当时思考了10来分钟一点没思路,后来全部都写完后,又来思考了半个小时,还是想不出来,就直接放弃了,后来看了大家的题解思路才知道是自己的物理学太菜了,大家在赛场上一定要有所取舍,填空题都是5分,如果不会就不要浪费太多时间。
C------好数
题目链接:https://www.lanqiao.cn/problems/19709/learning/
我在第十五届蓝桥杯C/C++B组国赛真题讲解(分享去年比赛的一些真实感受)-CSDN博客也讲过
一、解题思路
本题的目标是计算从 1 到给定正整数 N 范围内 "好数" 的数量。"好数" 的定义为:按从低位到高位的顺序,奇数位(个位、百位、万位......)上的数字是奇数,偶数位(十位、千位、十万位......)上的数字是偶数。
暴力思路的核心在于遍历从 1 到 N 的每一个整数,针对每个整数,逐一检查其每一位数字是否符合 "好数" 的定义。若符合,则将 "好数" 的计数加 1;若不符合,则继续检查下一个整数。最后,计数的结果即为从 1 到 N 中 "好数" 的总数。
二、代码展示
cpp
#include<bits/stdc++.h>//库函数,万能头,记住就好
using namespace std;//模板记
//fun(int i)函数用来判断传进来的参数i是否是好数,是返回true,否则返回false
bool fun(int i)
{
//count用来记录当前要判断的是位数
int count=1;
//若i等于0则循环结束
while(i)
{
//若count为奇数位,则count%2=1
if(count%2)
{
//a%10取当前最后一位数字,再对2求余
//奇数位需要是奇数,若是偶数,则当前位不符合
if(i%10%2==0)return false;
}
//如果为偶数位
else
{
//偶数位需要是偶数,若是奇数,则当前位不符合
if(i%10%2==1)return false;
}
//每次位数+1
count++;
//i每次要舍掉最后一位
i/=10;
}
//若每一位都判断完成后都没有return false,说明此数是好数
return true;
}
int main()
{
int n;
cin>>n;
int sum=0;
for(int i=1;i<=n;i++)if(fun(i))sum++;
cout<<sum;
return 0;
}
三、感悟
纯暴力,签到题
D------R格式
题目链接:https://www.lanqiao.cn/problems/19710/learning/



一、解题思路
本题要求根据给定的转换参数 n ,将浮点数 d 按照特定规则转换为 R 格式整数 。规则是先将浮点数乘以 2^n ,再四舍五入到最接近的整数。解题的主要步骤就是实现这个乘法和四舍五入操作,但是这个乘法要用高精度,用普通的long long int会超。
二、代码展示
cpp
#include<bits/stdc++.h>
using namespace std;
// 函数fun用于将字符串表示的数乘以2
string fun(string s)
{
int n = s.size();
int jinwei = 0; // 用于记录进位
for (int i = 0; i < n; i++)
{
if (s[i] == '.') continue; // 遇到小数点跳过
int temp = int(s[i] - '0'); // 将字符形式的数字转换为整型
int neww = temp * 2 + jinwei; // 当前位乘以2并加上进位
jinwei = neww / 10; // 计算新的进位
neww %= 10; // 取个位作为当前位新的值
s[i] = char(neww + '0'); // 将新值转换回字符形式存回原字符串
}
if (jinwei > 0) s += char(jinwei + '0'); // 如果最后还有进位,添加到字符串末尾
return s;
}
// 函数fun2用于对字符串表示的数进行四舍五入
string fun2(string s)
{
reverse(s.begin(), s.end()); // 反转字符串,方便从低位开始处理
int i = 0;
while (s[i] != '.') i++; // 找到小数点位置
i++; // 移动到小数点后一位
int jinwei = 1; // 初始进位为1,模拟进位操作
while (jinwei)
{
int temp = s[i] - '0' + 1; // 当前位数字加1(准备进位)
jinwei = temp / 10; // 计算新的进位
temp %= 10; // 取个位作为当前位新的值
s[i] = temp + '0'; // 将新值转换回字符形式存回原字符串
i++; // 处理下一位
}
reverse(s.begin(), s.end()); // 再反转回原顺序
return s;
}
int main()
{
int n;
string s;
cin >> n >> s; // 输入转换参数n和浮点数s(以字符串形式存储)
reverse(s.begin(), s.end()); // 反转字符串,方便后续处理
while (n--)
{
s = fun(s); // 循环n次,每次将字符串表示的数乘以2
}
int count = 0;
while (s[count] == '0' || s[count] == '.')
{
count++; // 跳过前导0和小数点
}
reverse(s.begin(), s.end()); // 再次反转回原顺序
int nLen = s.size() - count; // 得到有效数字部分的长度
int sign = 0;
for (int i = 0; i < nLen; i++)
{
if (s[i] == '.') sign = int(s[i + 1] - '0'); // 找到小数点,记录小数点后一位数字
}
if (sign == 0)
{
for (int i = 0; i < nLen; i++) cout << s[i]; // 小数点后一位是0,直接输出有效数字部分
}
else
{
if (sign >= 5)
{
s = fun2(s); // 小数点后一位大于等于5,进行四舍五入
for (int i = 0; i < nLen; i++)
{
if (s[i] == '.') break; // 遇到小数点停止输出
else cout << s[i]; // 输出四舍五入后的整数部分
}
}
else
{
for (int i = 0; i < nLen; i++)
{
if (s[i] == '.') break; // 小数点后一位小于5,直接输出整数部分
else cout << s[i];
}
}
}
return 0;
}
注意:我的代码不知道为什么只能过90%的样例,可能还有10%的情况我没有考虑到,求大神告知
三、感悟
这一题的话如果学了高精度的话并不难,如果没有学的话,可以直接用暴力,最好先用快速幂求出2^n,然后乘以d,四舍五入判断进行强制类型转换,判断是否加1就行
E------宝石组合
题目链接:https://www.lanqiao.cn/problems/19711/learning/

一、解题思路
(由于其他同学讲的特别好,所以我借鉴过来了)

二、代码示例
cpp
#include<stdio.h>
const int h=1e5;
int main(){
int n;
scanf("%d",&n);
int mp[h+1]={0};//初始化宝石闪亮度统计表
for(int i=0;i<n;i++){
int t;
scanf("%d",&t);
mp[t]++;//统计亮度为t的宝石数量
}
//这里我们另辟蹊径,直接枚举精美程度
for(int i=h;i>=1;i--){//枚举精美程度i
int ans=0,now=0;//ans表示寻找到了几个宝石,now表示现在数组有几个宝石
int num[3];//初始化枚举到的宝石
for(int j=i;j<=h;j+=i){//对于每个精美度i,我们都需要寻找闪亮度为i,2i,3i...的宝石并统计数量
ans+=mp[j];//把寻找到的宝石数量统计起来
for(int k=0;k<mp[j]&&now<3;k++){//把统计到的宝石放到数组
num[now]=j;
now++;
}
if(ans>=3){//如果找到了三个以上的宝石,说明存在三个宝石使其精美度为i
for(int k=0;k<3;k++){
printf("%d ",num[k]);
}//输出找到的三个宝石
printf("\n");
return 0;
}
}
}
}
三、感悟
这题的做法也非常巧妙,我当时正赛的时候是暴力枚举,取三个宝石算s的,但是这题这样去枚举精美程度可能会更简单一些,时间复杂度少了很多,从O(N^3)降到了O(N^2),有点像国赛的立定跳远,去枚举遍历你所要求的那一个值。
F------数字接龙
题目链接:https://www.lanqiao.cn/problems/19712/learning/
我在https://blog.csdn.net/SUN19326410095/article/details/147070595也讲过
一、解题思路
本题可通过深度优先搜索(DFS)来求解。由于要在 N×N的棋盘上,从左上角(0, 0)出发找到满足特定规则到达右下角(N - 1, N - 1)的路径,DFS 适合这种在多种可能路径中进行探索的场景。游戏规则要求路径数字按0到(K - 1)循环,且每个格子仅经过一次、路径不交叉,所以在 DFS 过程中,从起始点开始,每到一个格子,需按 8 个方向(水平、垂直、对角线)去探索新格子。对于每个新格子,要判断是否在棋盘内,防止越界;检查是否已访问,保证每个格子只走一次;确认数字是否符合循环序列,确保路径数字规则正确;还要查看路径是否交叉,满足所有这些条件才能继续递归探索。持续此过程,要么找到符合规则的路径,若有多条则按字典序选取最小的输出,要么确定不存在路径时输出-1 。
二、代码展示
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 11; // 定义棋盘的最大大小
int n, k; // n 为棋盘大小,k 为数字循环的范围
int board[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 visited[N][N]; // 标记棋盘上的格子是否被访问过
bool edge[N][N][N][N]; // 检查路径是否交叉
// 深度优先搜索函数,用于寻找路径
bool dfs(int x, int y) {
// 如果到达右下角格子,检查路径长度是否为 n*n - 1(因为起点不计入路径)
if (x == n - 1 && y == n - 1) {
return path.size() == n * n - 1;
}
visited[x][y] = true; // 标记当前格子已访问
for (int i = 0; i < 8; i++) { // 遍历 8 个方向
int newX = x + dx[i];
int newY = y + dy[i];
// 检查目标格子是否越界、是否访问过、数字是否满足循环序列要求
if (newX < 0 || newX >= n || newY < 0 || newY >= n) continue;
if (visited[newX][newY]) continue;
if (board[newX][newY] != (board[x][y] + 1) % k) continue;
// 检查路径是否交叉(对于斜向移动,检查是否有反向的路径)
if (edge[x][newY][newX][y] || edge[newX][y][x][newY]) continue;
edge[x][y][newX][newY] = true; // 标记路径
path += i + '0'; // 将方向编号加入路径
if (dfs(newX, newY)) return true; // 递归搜索下一个格子
path.pop_back(); // 回溯,移除路径中的最后一个方向
edge[x][y][newX][newY] = false; // 回溯,取消路径标记
}
visited[x][y] = false; // 回溯,取消当前格子的访问标记
return false; // 如果所有方向都无法到达终点,返回 false
}
int main() {
cin >> n >> k; // 输入棋盘大小和数字循环范围
for (int i = 0; i < n; i++) { // 读取棋盘上的数字
for (int j = 0; j < n; j++) {
cin >> board[i][j];
}
}
// 从起点 (0, 0) 开始搜索路径
if (!dfs(0, 0)) {
cout << -1 << endl; // 如果没有找到路径,输出 -1
} else {
cout << path << endl; // 输出路径的方向编号序列
}
return 0;
}
三、感悟
dfs和bfs年年必考,最近几年dfs考的最多,像这种题目思路都很类似,很容易掌握
G------爬山
我在蓝桥杯C/C++5天逆袭省一之第二天------C++STL容器(增强版)-CSDN博客讲过
一、解题思路

二、代码展示
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, P, Q;
long long ans = 0;
// 定义一个大顶堆h,用于存储山的高度,方便每次取出最高的山
priority_queue<int> h;
// 读取山的数量n,第一种魔法的可用次数P,第二种魔法的可用次数Q
cin >> n >> P >> Q;
for (int i = 0; i < n; i++)
{
int hi;
// 读取每座山的高度hi
cin >> hi;
// 将山的高度hi加入大顶堆h中
h.push(hi);
}
// 当两种魔法还有可用次数时,循环进行操作
while (P || Q)
{
// 取出堆顶元素,即当前最高的山的高度
int first = h.top();
h.pop();
// 如果两种魔法都有可用次数
if (P && Q)
{
// 比较两种魔法作用后的山的高度,选择能使山变得更矮的魔法
if (sqrt(first) <= first / 2)
{
// 使用第一种魔法,将山的高度变为其平方根向下取整
h.push(sqrt(first));
// 第一种魔法可用次数减1
P--;
}
else
{
// 使用第二种魔法,将山的高度变为其一半向下取整
h.push(first / 2);
// 第二种魔法可用次数减1
Q--;
}
}
// 如果只剩下第一种魔法有可用次数
else if (P)
{
// 使用第一种魔法,将山的高度变为其平方根向下取整
h.push(sqrt(first));
// 第一种魔法可用次数减1
P--;
}
// 如果只剩下第二种魔法有可用次数
else if (Q)
{
// 使用第二种魔法,将山的高度变为其一半向下取整
h.push(first / 2);
// 第二种魔法可用次数减1
Q--;
}
}
// 遍历堆,将剩余山的高度累加起来,得到最终花费的体力值
while (!h.empty())
{
ans += h.top();
h.pop();
}
// 输出最优情况下需要花费的体力值
cout << ans;
return 0;
}
注意:这一题可能只能通过90%的样例,并不能通过全部样例
三、感悟
这一题也会有少量数据通过不了,例如2,1,3,35,36,欢迎大神来指教一下
H------拔河
我在蓝桥杯C/C++5天逆袭省一之第二天------C++STL容器(增强版)-CSDN博客讲过
一、解题思路
二、代码展示
cpp
#include<bits/stdc++.h>
using namespace std;
// 定义数组的最大长度
const int N = 1e3+10;
// 存储前缀和数组
long long a[N];
// 同学的数量
int n;
// 用于存储所有可能的右区间的力量值之和
multiset<long long>s;
// 自定义函数,返回两个数中的较小值
long long minn(long long a,long long b){
if(a<b) return a;
else return b;
}
int main(){
// 读取同学的数量
cin>>n;
// 读取每个同学的力量值,并构建前缀和数组
for(int i = 1;i<=n;i++) {
cin>>a[i];
// 计算前缀和
a[i]+=a[i-1];
}
// 枚举所有可能的右区间 [i, j],并将其力量值之和插入到 multiset 中
for(int i = 1;i<=n;i++){
for(int j = i;j<=n;j++){
// 计算右区间 [i, j] 的力量值之和并插入到 multiset 中
s.insert(a[j]-a[i-1]);
}
}
// 初始化最小差距为一个较大的值
long long res = 1e9;
// 枚举左区间的右端点 i
for(int i = 1;i<n;i++){
// 删除所有以 i 作为右区间起始点的情况,避免左区间和右区间重叠
for(int j = i;j<=n;j++){
// 计算以 i 为起始点的右区间 [i, j] 的力量值之和
auto k = a[j] - a[i-1];
// 从 multiset 中删除该力量值之和
s.erase(s.find(k));
}
// 枚举左区间的左端点 j
for(int j = 1;j<=i;j++){
// 计算左区间 [j, i] 的力量值之和
auto k = a[i] - a[j-1];
// 在 multiset 中找到第一个大于等于 k 的元素
auto p = s.lower_bound(k);
// 如果找到了这样的元素,计算其与 k 的差值的绝对值,并更新最小差距
if(p!=s.end()){
res = minn(res,abs(*p-k));
}
// 如果 p 不是 multiset 的第一个元素,考虑其前一个元素与 k 的差值的绝对值,并更新最小差距
if(p!=s.begin()){
p--;
res = minn(res,abs(*p-k));
}
}
}
// 输出最小差距
cout<<res<<endl;
return 0;
}
三、感悟:
有同学在后台询问我拔河这道题56-58行代码为什么还要判断,因为在前面lower_bound找到的只是在大于等于k的数里面最接近k的数,但我们实际上还需要找小于等于k的数里面最接近k的数,将它们差值的绝对值进行比较,因为我们要求的是俩区间差值的最小值
总结:
省赛来临之际,希望大家在学完相关算法知识后,一定要及时的去训练刷题,如果有什么疑问,可以在评论区留言,考前可以去一下我之前写的蓝桥杯C/C++实战经验分享-CSDN博客,里面分享了一些考试小技巧,希望大家取得好的比赛成绩!!