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

2024蓝桥杯省B好题分析

题解来自洛谷,作为学习

目录

宝石组合

数字接龙

爬山

拔河


宝石组合

# [蓝桥杯 2024 省 B] 宝石组合## 题目描述在一个神秘的森林里,住着一个小精灵名叫小蓝。有一天,他偶然发现了一个隐藏在树洞里的宝藏,里面装满了闪烁着美丽光芒的宝石。这些宝石都有着不同的颜色和形状,但最引人注目的是它们各自独特的 “闪亮度” 属性。每颗宝石都有一个与生俱来的特殊能力,可以发出不同强度的闪光。小蓝共找到了 $n$ 枚宝石,第 $i$ 枚宝石的 “闪亮度” 属性值为 $H_i$,小蓝将会从这 $n$ 枚宝石中选出三枚进行组合,组合之后的精美程度 $S$ 可以用以下公式来衡量:$$
S = H_a H_b H_c \cdot \frac{\operatorname{LCM}(H_a, H_b, H_c)}{\operatorname{LCM}(H_a, H_b) \cdot\operatorname{LCM}(H_a, H_c) \operatorname{LCM}(H_b, H_c)}
$$其中 $\operatorname{LCM}$ 表示的是最小公倍数函数。小蓝想要使得三枚宝石组合后的精美程度 $S$ 尽可能的高,请你帮他找出精美程度最高的方案。如果存在多个方案 $S$ 值相同,优先选择按照 $H$ 值升序排列后字典序最小的方案。## 输入格式第一行一个整数 $n$ 表示宝石个数。  
第二行有 $n$ 个整数 $H_1, H_2, \dots H_n$ 表示每个宝石的闪亮度。## 输出格式输出一行包含三个整数表示满足条件的三枚宝石的 “闪亮度”。## 样例 #1### 样例输入 #1```
5
1 2 3 4 9
```### 样例输出 #1```
1 2 3
```## 提示### 数据规模与约定- 对 $30\%$ 的数据,$n \leq 100$,$H_i \leq 10^3$。
- 对 $60\%$ 的数据,$n \leq 2 \times 10^3$。
- 对全部的测试数据,保证 $3 \leq n \leq 10^5$,$1 \leq H_i \leq 10^5$。

做题思路

1.找一个 cnt[i]cnt[i] 用于记录从 H1,H2H1​,H2​ 一直到 HnHn​ 中能被 ii 整除的个数。
2.找到最大的 ii 使得 cnt[i]≥3cnt[i]≥3 记为 xx
3.从小到大排序 HH 数组。
4.从 11 到 nn 遍历,判断 Hi∣xHi​∣x ,输出前 3 个满足要求的数。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int h[N],cnt[N];
int main()
{int n,x,c=0;cin>>n;for(int i=1;i<=n;i++){cin>>h[i];for(int j=1;j*j<=h[i];j++) {if(h[i]%j==0){cnt[j]++;if(j*j!=h[i]) cnt[h[i]/j]++; }}}for(int i=N-5;i>=1;i--){if(cnt[i]>=3){x=i;break;}}sort(h+1,h+n+1);for(int i=1;i<=n;i++){if(h[i]%x==0){cout<<h[i]<<" ";c++;}if(c==3) return 0;}return 0;
}

代码思路:

  1. 输入处理
    • 先读取输入,存储每个宝石的“闪亮度”到数组 h 中。
  2. 统计每个公约数的出现次数
    • 代码核心在于使用 cnt[] 数组来统计每个数作为公约数出现的次数。
    • 对每个宝石 h[i],你通过枚举它的每个约数 j 来统计该约数出现的次数。对于约数 j 和对应的商 h[i]/j,都进行计数,这样可以有效统计每个数在所有宝石中的公约数出现的次数。
  3. 找到最多次作为公约数出现的数
    • 在统计完成后,代码从大到小查找第一个至少出现 3 次的公约数(即 cnt[i] >= 3),并将这个数记录为 x
  4. 输出满足条件的 3 个宝石
    • 最后,你对宝石数组 h 进行排序,并输出前 3 个能被 x 整除的宝石。

优点:

  1. 高效的公约数统计

    • 你使用了枚举约数的技巧,对于每个 h[i],你仅需要枚举到 sqrt(h[i]),所以这部分的时间复杂度是较为高效的。总体上,该部分的时间复杂度为 O(n×h[i])O(n \times \sqrt{h[i]})O(n×h[i]​),对于 h[i] 较大的情况可以有效应对。
  2. 贪心的寻找公约数

    • 你从大到小查找第一个至少出现 3 次的公约数,保证了找到的公约数是最大的,从而能够保证美丽度的最大化。
  3. 简洁的实现

    • 代码简洁且逻辑清晰,从输入处理、到统计公约数再到排序输出,结构明了,易于理解。

数字接龙

#include <bits/stdc++.h>
#define pp ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
using namespace std;
int n,k;
const int N = 30;
int f[N][N];  
string ans;
bool xie[N][N][N][N],ji[N][N];
int dx[8]={-1,-1,0,1,1,1,0,-1},dy[8]={0,1,1,1,0,-1,-1,-1};    //特别注意要按照题目给的要求来,方便后面直接把i当作八个方向的编号 
void dfs(int a,int b,int c,int zong){              //坐标x,y,数,总步数if (a==n-1 && b==n-1 && zong==pow(n,2)-1){     //当走到(n-1,n-1),步数为总格子数减一时(左上角一开始的(0,0)不用走)if (ans.empty()) cout << -1;               for (int i=0;i<zong;i++) cout << ans[i];exit(0);                                   //输出完答案直接关闭程序 }for (int i=0;i<8;i++){int x=dx[i]+a,y=dy[i]+b;if(f[x][y]==(c+1)%k){if (x>=0 && y>=0 && x<n && y<n && !ji[x][y]){int lu=0;                            //用来记录用不用走斜线 if (i%2==1){                         //如果走的是斜线需要判断 if(!xie[a][y][x][b]){            //另一条斜线没走过就能走 lu=1;xie[a][b][x][y]=true;        xie[x][y][a][b]=true;}else continue;                   //与其它交叉就跳过这条路 }ans.push_back(i+'0');ji[x][y] = true;dfs(x, y,(c+1)%k,zong+1);ji[x][y] = false;                    //回溯 ans.pop_back();if(lu & 1){                          //用到斜线的话需要单独回溯 xie[a][b][x][y]=false;xie[x][y][a][b]=false;}}}}
}
int main()
{pp;                            //用到dfs怕超时,先关一手 cin >> n >> k;for (int i = 0; i < n; i++) {for (int j = 0; j < n;j++) cin >> f[i][j];}ji[0][0] = 1;                  //不要忘记左上角是默认走过的 dfs(0,0,0,0);                  //开始dfscout << -1;                    //还有种情况是当走不到(n-1,n-1)时,输出-1return 0;
}

思路分析:

  1. 基础概念

    • 使用深度优先搜索(DFS)来遍历所有可能的路径。
    • 迷宫是一个 N x N 的棋盘,每个格子里有一个数字。
    • 每一步可以按照八个方向(水平、垂直和对角线)移动到相邻的格子。
    • 在移动时,当前格子中的数字要满足从0到K-1的循环条件,即走到下一个格子的数字必须是 (当前数字 + 1) % K
  2. DFS的递归逻辑

    • 从左上角 (0, 0) 开始,每次移动到符合规则的下一个格子,并且检查这条路径是否可以到达右下角 (n-1, n-1)
    • 记录已经走过的格子,避免重复访问。
    • 对于斜线移动,题目要求两条斜线不能交叉。因此,代码中对斜线的情况进行了特别的处理,用 xie 数组记录是否有交叉的斜线。
  3. 边界条件处理

    • 当遍历到右下角 (n-1, n-1) 并且走过所有格子(步数为 n*n - 1)时,输出路径。
    • 如果尝试了所有可能的路径仍无法找到合法解,则输出 -1

代码详细分析:

  1. 坐标移动方向定义

    • dxdy 数组定义了八个方向,顺序与题目中的顺序保持一致,便于之后直接使用这些方向来移动。
  2. DFS核心部分

    • dfs 函数中,首先判断是否达到了 (n-1, n-1) 并且是否走满了所有格子(总步数为 n*n - 1)。
    • 如果条件满足,输出结果并退出程序。
    • 然后,对八个方向进行尝试移动,确保移动符合条件:格子没有访问过,数字满足条件,且斜线不能与已有斜线交叉。
    • 如果移动成功,继续递归进行深度搜索。搜索完成后进行回溯,恢复状态,以便探索其他可能的路径。
  3. 边界检查

    • 移动过程中对坐标的合法性进行检查,确保不会超出棋盘的边界。
  4. 优化措施

    • 通过 pp 宏定义加快输入输出的速度,避免DFS过程中超时。
    • 一旦找到解,直接退出程序,避免继续多余的计算。

优点:

  1. 思路清晰:代码利用DFS遍历所有可能的路径,并通过条件判断确保每条路径的合法性,思路比较清晰。
  2. 边界处理完备:考虑了各种边界条件,包括边界越界、斜线交叉、数字循环等问题。
  3. 剪枝回溯:利用回溯法在DFS的基础上进行了路径选择和状态恢复,避免了重复计算和死循环。

爬山

贪心思路,分别求出 第一种魔法的结果sqrt_res,第二种魔法的结果div_res,比较谁更小,再决定使用第几种魔法!!!
请特别注意 p == 0 && q != 0、q == 0 && p != 0这两种情况,不需要比较,直接使用有次数的魔法即可。
次数都为0时,跳出while循环痛哭,比赛的时候紧张,忘记优先队列头文件了,想着for循环遍历一下找最大数很快,没有使用大根堆。#include<iostream>
#include <queue>
#include <math.h>
using namespace std;
int n,p,q;
priority_queue<int> mount;
int main()
{scanf("%d %d %d",&n,&p,&q);int t;for(int i = 0;i < n;i++){scanf("%d",&t);mount.emplace(t);}while(p || q){int tmp = mount.top();mount.pop();int sqrt_res = sqrt(tmp),div_res = tmp / 2;if(p)//如果p有次数{if(q)//如果q也有次数,需要进行比较{if(sqrt_res <= div_res){mount.emplace(sqrt_res);p--;}}   else//q没有次数,直接emplace,不需要比较{mount.emplace(sqrt_res);p--;} }else//p没次数{if(q){mount.emplace(div_res);q--;}else//都没有次数,跳出循环break;}}long long res = 0;while (!mount.empty()) {res += mount.top();mount.pop();}cout << res << endl;return 0;
}

代码分析:

  1. 数据输入与初始化
    • 首先读取输入的三个参数 npq,然后读取每座山的高度,并将它们依次存入最大堆 mount 中。这个堆会在每次操作时帮助我们迅速找到当前高度最大的山峰。
  2. 主循环:选择和处理最大山峰
    • 你通过 while (p || q) 循环不断处理山峰,直到 p(第一种魔法的次数)或 q(第二种魔法的次数)用完为止。

    • 贪心选择:每次都选出当前最高的山峰,并将其从堆中弹出。然后根据当前的 pq 值,来决定使用哪种魔法。

    • 比较 sqrt_resdiv_res

      • 如果 pq 都有剩余,比较使用两种魔法后的高度,选择效果更好的那个:
        • 第一种魔法将山峰高度开平方,结果为 sqrt_res
        • 第二种魔法将山峰高度除以2,结果为 div_res
      • 如果 sqrt_res <= div_res,则使用第一种魔法,否则使用第二种。
    • 特殊情况

      • 如果 p == 0 && q != 0,则只使用第二种魔法。
      • 如果 q == 0 && p != 0,则只使用第一种魔法。
  3. 结果计算与输出
    • pq 都用完后,程序将剩下的山峰高度全部相加,作为最后的输出结果。

注意点:

  1. 优先队列的使用
    • 使用最大堆是一个非常好的贪心选择。因为每次都需要选择当前高度最高的山峰来操作,这个堆的性质可以确保我们在每次循环中都能快速找到最大值。
  2. 特殊情况处理
    • 代码中处理了 p == 0 && q != 0q == 0 && p != 0 的特殊情况,保证了在一方次数用尽后,不再进行不必要的比较,节省了时间。

拔河

前缀和思想,然后顺便记录每个队伍的区间,以及每个队伍的值,排序后求相邻区间的差值,如果区间没交集则有效,最后输出最小的
#include <bits/stdc++.h>
#define ll long long
#define PLL pair<ll,ll>
using namespace std;
int n;
const int N = 1e3+10;
ll d[N];
ll mi=LLONG_MAX;
struct h{ll a;int b;int c;
};
bool cmp(h x,h y){if(x.a==y.a) return x.b<y.b;if(x.a==y.a && x.b==y.b) return x.c<y.c;return x.a<y.a;
}
int main(){vector<h> s;scanf("%d",&n);for(int i=1;i<=n;i++){ll x;scanf("%lld",&x);d[i]=d[i-1]+x;}for(int i=0;i<n;i++){for(int k=i+1;k<=n;k++){s.push_back({d[k]-d[i],i,k});}}sort(s.begin(),s.end(),cmp);for(int i=1;i<s.size();i++){if((s[i].b>=s[i-1].c && s[i].c>=s[i-1].c) || (s[i-1].b>=s[i].c && s[i-1].c>=s[i].c))  mi=min(s[i].a-s[i-1].a,mi);else{for(int j=i+1;j<s.size();j++){if((s[i].b>=s[i-1].c && s[i].c>=s[i-1].c) || (s[i-1].b>=s[i].c && s[i-1].c>=s[i].c)){mi=min(s[j].a-s[i].a,mi);break;}if((s[j].a-s[i].a)>=mi) break;}}}printf("%lld",mi);return 0;
}

代码分析:

  1. 前缀和的使用

    • 你通过计算前缀和数组 d[] 来快速获得任何区间的和。具体来说,对于区间 [i, k] 的和,可以用 d[k] - d[i] 来得到。这是经典的前缀和技巧,大大减少了重复计算区间和的复杂度。
  2. 区间值的存储

    • 使用结构体 h 来存储每个区间的信息,包括该区间的和 a,区间的起点 b,和终点 c。你将所有的可能区间都存入 vector 容器 s 中。
  3. 排序规则

    • 区间先按区间和 a 进行排序,如果和相同,则按区间起点 b 排序,起点相同再按终点 c 排序。这确保了在相同区间和的情况下,会按照从左到右的区间顺序来排序。
  4. 寻找最小差值

    • 经过排序后,你需要比较相邻的区间和是否有交集:
      • 只有当两个区间的终点和起点没有重叠时,才是有效的两个队伍(两个不相交的区间)。
      • 当发现相邻的两个区间不相交时,计算它们的区间和之差并更新最小差值 mi
  5. 边界处理和优化

    • 代码中包含了一层优化,即在比较两个区间时,如果当前的差值已经大于等于最小差值 mi,则可以提前终止循环,避免不必要的比较。

优点:

  1. 使用前缀和优化区间和的计算:前缀和的使用有效地减少了多次重复的区间和计算,保证了时间复杂度的优化。

  2. 巧妙的排序规则:通过对区间和的排序,并按起点、终点进行二次排序,简化了之后寻找最小差值的过程。

  3. 贪心思想的应用:你通过贪心思想,即优先处理差值较小的区间和,快速找到两个不相交区间的最小差值。


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

相关文章:

  • 翼鸥教育:从OceanBase V3.1.4 到 V4.2.1,8套核心集群升级实践
  • R语言机器学习与临床预测模型77--机器学习预测常用R语言包
  • 2024版本IDEA创建Sprintboot项目下载依赖缓慢
  • 开源 - Ideal库 - 常用枚举扩展方法(二)
  • 解决表格出现滚动条样式错乱问题
  • Nginx 的 proxy_pass 使用简介
  • 项目管理必备3大工具,助你的项目管理技能飞跃提升。
  • 深入解析 ArrayList 与 LinkedList:Java 集合框架中的两大常用 List
  • LDD学习启程(TODO)
  • 医学数据分析实训 项目七 集成学习--空气质量指标--天气质量分析和预测
  • Vue 3有哪些新特性
  • 大众点评代发排名骗局
  • SpringBoot:解析excel
  • KeyCode及KeyCode分发机制
  • C++ 科目二 [reinterpret_cast]
  • Python VS Golng 谁更胜一筹?
  • 代码随想录Day 49|leetcode题目:42.接雨水、84.柱状图中最大矩形
  • 论文阅读 | 基于流模型和可逆噪声层的鲁棒水印框架(AAAI 2023)
  • Java零基础-抽象类详解
  • python:Django与Celery配合实现定时任务
  • Linux(ubuntu)(C语言开发-下载篇)
  • flutter Dio发送post请求
  • 八戒农场小程序V2最新源码
  • React-Hook原理
  • 数据库系统原理与应用【笔记总结】
  • Linux(Ubuntu)(终端实现helloworld输出)