0


第十五届蓝桥杯C++B组省赛

在这里插入图片描述

文章目录

1.握手问题

题目描述:
在这里插入图片描述

解题思路1(组合数学)

按照题目描述来说,会议有五十人,如果不加任何限制条件,这五十个人两两握手的次数是:

  1. t
  2. o
  3. t
  4. a
  5. l
  6. =
  7. 49
  8. +
  9. 48
  10. +
  11. 47
  12. +
  13. .
  14. .
  15. .
  16. .
  17. .
  18. .
  19. .
  20. .
  21. +
  22. 1
  23. total=49+48+47+........+1
  24. total=49+48+47+........+1

利用高斯求和的得出:

  1. t
  2. o
  3. t
  4. a
  5. l
  6. =
  7. 50
  8. 49
  9. /
  10. 2
  11. total=50*49/2
  12. total=5049/2

如果加上限制条件的话,题目给定的其中有七个人不会相互握手,需要用上面总的不加限制的减去七个人相互握手的次数。

  1. c
  2. n
  3. t
  4. =
  5. 6
  6. +
  7. 5
  8. +
  9. .
  10. .
  11. .
  12. .
  13. .
  14. .
  15. +
  16. 1
  17. =
  18. 7
  19. 6
  20. /
  21. 2
  22. cnt=6+5+......+1=7*6/2
  23. cnt=6+5+......+1=76/2

上述两式作差即可
编写代码:

  1. #include<iostream>
  2. using namespace std;intmain(){int total =50*49/2;int cnt =7*6/2;
  3. cout << total - cnt << endl;return0;}

解题思路2(暴力枚举)

将每个人握手的情况枚举出来即可。

  1. #include<iostream>
  2. using namespace std;intmain(){int ans =0;for(int i =1;i <=50;i++){for(int j = i +1;j <=50;j++){//排除掉七人的情况if(!(i >=1&& i <=7&& j >=1&& j <=7)){
  3. ans++;}}}
  4. cout << ans << endl;return0;}

2.小球反弹

问题描述:
在这里插入图片描述

做题思路

这道题我们肯定不能直接做的,这道题给定了

  1. d
  2. x
  3. :
  4. d
  5. y
  6. dx:dy
  7. dx:dy的值说明这道题我们应该分解来做,将小球的反弹的路径分解为x方向和y方向来做。

我们首先假设x方向上经过了p个来回,y方向上经历了q个来回,因为是分解的缘故,所以两个分解方向上的时间是相同的。
所以可以得出两个等式:

  1. d
  2. x
  3. t
  4. =
  5. 2
  6. p
  7. x
  8. dx*t=2px
  9. dxt=2px(由于这里一半的路程是x,所以一个来回的路程是2x,乘以来回就是总路程)
  10. d
  11. y
  12. t
  13. =
  14. 2
  15. q
  16. x
  17. dy*t=2qx
  18. dyt=2qx

将这两个式子进行比例

  1. d
  2. x
  3. d
  4. y
  5. =
  6. p
  7. x
  8. q
  9. y
  10. \frac{dx}{dy}=\frac{px}{qy}
  11. dydx​=qypx

得到这个式子之后我们可以利用gcd对这个式子的左边进行约分。
可以得出:

  1. p
  2. =
  3. d
  4. x
  5. y
  6. p=dx*y
  7. p=dxy
  8. q
  9. =
  10. d
  11. y
  12. x
  13. q=dy*x
  14. q=dyx

算出q或者p之后可以利用公式计算t:

  1. t
  2. =
  3. 2
  4. p
  5. x
  6. /
  7. d
  8. x
  9. t=2px/dx
  10. t=2px/dx

最后得出总路程:

  1. 总路程
  2. =
  3. t
  4. (
  5. s
  6. q
  7. r
  8. t
  9. (
  10. 1
  11. 5
  12. 2
  13. +
  14. 1
  15. 7
  16. 2
  17. )
  18. )
  19. 总路程=t*(sqrt(15^2+17^2))
  20. 总路程=t∗(sqrt(152+172))

编写代码:

  1. //求最大公约数intgcd(int a,int b){return b ==0? a :gcd(b, a % b);}intmain(){//给出x方向和y方向的速度 int dx =15, dy =17;//给出x方向和y方向上的距离int x =343720, y =233333;//求出多少来回int q = dy * x, p = dx * y;//求最大公约数int g =gcd(p, q);
  2. p /= g, q /= g;//计算时间int t =2* p * x / dx;//求路程double ans = t *sqrt(15*15+17*17);printf("%.2lf\n", ans);return0;}

3.好数

问题描述:
在这里插入图片描述

数据量:
在这里插入图片描述

算法思路(暴力解法)—不会超时

遍历1到n的数,然后写一个check函数判断每个数是否是好数,这里的时间复杂度是

  1. n
  2. l
  3. o
  4. g
  5. n
  6. n*logn
  7. nlogn

编写代码:

  1. #include<iostream>
  2. using namespace std;int N,count;
  3. bool Check(int n){int i=1;while(n!=0){int tail=n%10;if(i%2==1){if(tail%2!=1)return false;}else{if(tail%2!=0)return false;}
  4. i++;
  5. n/=10;}return true;}intmain(){// 请在此输入您的代码
  6. cin>>N;for(int i=1;i<N;i++){if(Check(i)){
  7. count++;}}
  8. cout<<count<<endl;return0;}

4.R格式

题目描述:
在这里插入图片描述

数据量:
在这里插入图片描述

可以看到这道题的数据量是很大的,涉及到了幂次,肯定不可能直接去算,这道题很显然是考察的是高精度算法(高精度*低精度)

算法思路

高精度模版题:

编写代码:

  1. #include<iostream>#include<algorithm>#include<string>#include<cmath>
  2. using namespace std;//数组模拟高精度:高精度*低精度constint N =2e3+10;
  3. string s;int a[N];intmain(){int n;
  4. cin >> n >> s;//反转操作reverse(s.begin(), s.end());//确定小数点的位置int pos = s.find(".");
  5. s.erase(pos,1);//删除小数点,方便后续计算int len = s.size();for(int i =0;i < len;i++) a[i +1]= s[i]-'0';//高精度*低精度for(int i =1;i <= n;i++){//顺序扫描,均*2for(int j =1;j <= len;j++) a[j]*=2;//处理大于10的位数for(int j =1;j <= len;j++){if(a[j]>=10){
  6. a[j +1]++;
  7. a[j]%=10;if(j == len) len++;}}}//处理小数点后的第一位,进行四舍五入if(a[pos]>=5)a[pos +1]++;for(int i = pos +1;i <= len;i++){if(a[i]>=10){
  8. a[i +1]++;
  9. a[i]%=10;if(i == len)len++;}}//打印的时候倒序打印for(int i = len;i >= pos +1;i--) cout << a[i];return0;}

5.宝石组合

题目描述:

在这里插入图片描述

数据范围:
在这里插入图片描述

首先从数据量来看这道题是不能用暴力枚举的,因为暴力枚举三个数的时间复杂度是

  1. O
  2. (
  3. N
  4. 3
  5. )
  6. O(N^3)
  7. O(N3)了。

算法思路—唯一分解定理

首先我们要知道什么是唯一分解定理,简单来说唯一分解定理就是,任意一个大于1的正整数 ,都可以唯一地表示为若干个质数的乘积,且这些质数的顺序不影响分解的唯一性。
那么可以得出:

  1. N
  2. 1
  3. =
  4. p
  5. 1
  6. a
  7. 1
  8. p
  9. 2
  10. a
  11. 2
  12. p
  13. n
  14. a
  15. n
  16. N_1 = p_1^{a_1} \cdot p_2^{a_2} \cdot \ldots \cdot p_n^{a_n}
  17. N1​=p1a1​​⋅p2a2​​⋅…⋅pnan​​
  18. N
  19. 2
  20. =
  21. p
  22. 1
  23. b
  24. 1
  25. p
  26. 2
  27. b
  28. 2
  29. p
  30. n
  31. b
  32. n
  33. N_2 = p_1^{b_1} \cdot p_2^{b_2} \cdot \ldots \cdot p_n^{b_n}
  34. N2​=p1b1​​⋅p2b2​​⋅…⋅pnbn​​

从上面两个式子可以得出:

  1. gcd
  2. (
  3. N
  4. 1
  5. ,
  6. N
  7. 2
  8. )
  9. =
  10. p
  11. 1
  12. min
  13. (
  14. a
  15. 1
  16. ,
  17. b
  18. 1
  19. )
  20. p
  21. 2
  22. min
  23. (
  24. a
  25. 2
  26. ,
  27. b
  28. 2
  29. )
  30. p
  31. n
  32. min
  33. (
  34. a
  35. n
  36. ,
  37. b
  38. n
  39. )
  40. \gcd(N_1,N_2) = p_1^{\min(a_1,b_1)} \cdot p_2^{\min(a_2,b_2)} \cdot \ldots \cdot p_n^{\min(a_n,b_n)}
  41. gcd(N1​,N2​)=p1min(a1​,b1​)​⋅p2min(a2​,b2​)​⋅…⋅pnmin(an​,bn​)​
  42. lcm
  43. (
  44. N
  45. 1
  46. ,
  47. N
  48. 2
  49. )
  50. =
  51. p
  52. 1
  53. max
  54. (
  55. a
  56. 1
  57. ,
  58. b
  59. 1
  60. )
  61. p
  62. 2
  63. max
  64. (
  65. a
  66. 2
  67. ,
  68. b
  69. 2
  70. )
  71. p
  72. n
  73. max
  74. (
  75. a
  76. n
  77. ,
  78. b
  79. n
  80. )
  81. \operatorname{lcm}(N_1,N_2) = p_1^{\max(a_1,b_1)} \cdot p_2^{\max(a_2,b_2)} \cdot \ldots \cdot p_n^{\max(a_n,b_n)}
  82. lcm(N1​,N2​)=p1max(a1​,b1​)​⋅p2max(a2​,b2​)​⋅…⋅pnmax(an​,bn​)​

假设Ha,Hb,Hc的分解出来的相同的质数的幂次分别是x,y,z那么可以得出:

在这里插入图片描述

上面的式子可以转换为幂次是:

  1. x
  2. +
  3. y
  4. +
  5. z
  6. +
  7. max
  8. (
  9. x
  10. ,
  11. y
  12. ,
  13. z
  14. )
  15. max
  16. (
  17. x
  18. ,
  19. y
  20. )
  21. max
  22. (
  23. x
  24. ,
  25. z
  26. )
  27. max
  28. (
  29. y
  30. ,
  31. z
  32. )
  33. x+y+z+\max(x,y,z)-\max(x,y)-\max(x,z)-\max(y,z)
  34. x+y+z+max(x,y,z)−max(x,y)−max(x,z)−max(y,z)

相当于我们只需要求出上面式子的最大值即可。

编写代码:

  1. #include<iostream>#include<vector>#include<algorithm>
  2. using namespace std;constint N =1e5+10;int a[N];//fac是存储因子的二维数组,s是求的最大值
  3. vector<int> fac[N], s[N];intmain(){//遍历数组for(int i =1;i <=1e5;i++){for(int j = i;j <=1e5;j += i){//i是j的因子
  4. fac[j].push_back(i);}}//输入n个数int n;cin >> n;for(int i =1;i <= n;i++)cin >> a[i];//保证字典序最小sort(a +1, a + n +1);for(int i =1;i <= n;i++){//处理i的每个因子for(int j =0;j < fac[a[i]].size();j++){//
  5. s[fac[a[i]][j]].push_back(a[i]);}}for(int i =1e5;i >=0;i--){if(s[i].size()>=3){
  6. cout << s[i][0]<<' '<< s[i][1]<<' '<< s[i][2]<< endl;break;}}return0;}

6.数字接龙

问题描述:

在这里插入图片描述
在这里插入图片描述
数据量:
在这里插入图片描述
根据数据量来看这道题考察的应该是DFS,但是在DFS中应该还涉及到回溯,因为当走到不满足条件的时候需要进行回溯。

算法思路----DFS

编写代码:

  1. #include<iostream>#include<string>
  2. using namespace std;constint N =20;int a[N][N];
  3. bool vis[N][N];int n, k;//方向数组: 0 1 2 3 4 5 6 7int dx[8]={-1,-1,0,1,1,1,0,-1};int dy[8]={0,1,1,1,0,-1,-1,-1};
  4. string res;voiddfs(int x,int y,int prev, string s,int dep){//当搜到终点的时候,且搜索深度是n的时候,意思就是每种情况都搜索完了if(x == n && y == n && dep == n * n){if(res.empty())res = s;return;}for(int i =0;i <8;i++){//生成邻接点int bx = x + dx[i];int by = y + dy[i];//过滤越界节点if(bx<1|| bx>n || by<1|| by>n)continue;//过滤访问过的节点if(vis[bx][by]== true)continue;//防止交叉搜索if(i ==1&& vis[x -1][y]&& vis[x][y +1])continue;if(i ==3&& vis[x +1][y]&& vis[x][y +1])continue;if(i ==5&& vis[x +1][y]&& vis[x][y -1])continue;if(i ==7&& vis[x -1][y]&& vis[x][y -1])continue;//保证路径数值为0 1 2 3 .....k-1if((a[bx][by]< k && a[bx][by]== prev +1)||(prev +1== k && a[bx][by]==0)){//可以搜索,将点标记为true
  5. vis[bx][by]= true;dfs(bx, by, a[bx][by], s +to_string(i), dep +1);//最优性剪枝if(!res.empty())return;
  6. vis[bx][by]= false;//回溯}}}intmain(){
  7. cin >> n >> k;for(int i =1;i <= n;i++)for(int j =1;j <= n;j++)
  8. cin >> a[i][j];
  9. string emp;//标记起点
  10. vis[1][1]= true;dfs(1,1,0, emp,1);if(res.empty())cout <<-1;else cout << res << endl;return0;}

7.拔河

问题描述:
在这里插入图片描述
数据量:
在这里插入图片描述
对于这种涉及到区间和的题来说,大概率都是用前缀和算法解决

算法思路

编写代码:

  1. #include<iostream>#include<set>#include<climits>
  2. using namespace std;#definelllonglongconstint N =1e3+10;int a[N], s[N];//前缀和和数组
  3. multiset<int> ms;intmain(){int n;cin >> n;for(int i =1;i <= n;i++){
  4. cin >> a[i];//前缀和
  5. s[i]= s[i -1]+ a[i];}//用set去维护所有的区间和for(int i =1;i <= n;i++){for(int j =1;j <= n;j++){//维护区间和
  6. ms.insert(s[j]- s[i -1]);}}int ans = LONG_MAX;for(int i =1;i <= n;i++){for(int j =1;j < i;j++){//枚举以i结尾的区间,因为这里i-1只会有一个人,所以应该是j-1int sum = s[i]- s[j -1];//找到与该区间和sum相似的区间和auto it = ms.lower_bound(sum);if(it != ms.end()){
  7. ans =min(ans,abs(*it - sum));}if(it != ms.begin()){
  8. it--;
  9. ans =min(ans,abs(*it - sum));}}//删除以i开头且以j结尾的区间,防止后续查询区间的时候出现区间重叠/交叉问题for(int j = i;j <= n;j++) ms.erase(ms.find(s[j]- s[i -1]));}
  10. cout << ans << endl;return0;}
标签: 蓝桥杯 c++ 算法

本文转载自: https://blog.csdn.net/2301_79969994/article/details/142898129
版权归原作者 lyyyyrics 所有, 如有侵权,请联系我们删除。

“第十五届蓝桥杯C++B组省赛”的评论:

还没有评论