当前位置:   article > 正文

蓝桥杯2020年第十一届C/C++B组(第二次)省赛习题题解_蓝桥杯2020年省赛b组

蓝桥杯2020年省赛b组

目录

试题A.门牌制作(拆分数字)

试题 B 既约分数(gcd)

试题C 蛇形填数(数学)

试题D 跑步锻炼(模拟) 

试题E 七段码(图论+并查集)

 试题F:成绩统计(格式化输出)

 试题G:回文日期(模拟)

试题H:子串分值和(哈希+乘法原理+数学)

试题I:平面切分(数学)

 试题J:字串排序(dp)



试题来源:蓝桥杯2020第十一届C语言B组省赛习题题解_shall_zhao的博客-CSDN博客_蓝桥杯c语言题目


试题A.门牌制作(拆分数字)

 思路解析:遍历1~2020,统计每个数字中2的次数,运用到拆分数字的基本算法

  1. #include<iostream>
  2. using namespace std;
  3. int cal(int num)
  4. {
  5. int cnt = 0;
  6. while (num)
  7. {
  8. int j = num % 10;
  9. if (j == 2) cnt++;
  10. num /= 10;
  11. }
  12. return cnt;
  13. }
  14. int main()
  15. {
  16. int res = 0;
  17. for (int i = 1; i <= 2020; i++)
  18. {
  19. res += cal(i);
  20. }
  21. cout << res << endl;//答案为624
  22. return 0;
  23. }

试题 B 既约分数(gcd

 思路解析:两层内外循环遍历1~2020,用欧几里得算法判断是否是两数的最大公约数为1,统计个数即可

  1. #include<iostream>
  2. using namespace std;
  3. int gcd(int a, int b)
  4. {
  5. return b ? gcd(b, a % b) : a;
  6. }
  7. int main()
  8. {
  9. int res = 0;
  10. for(int i=1;i<=2020;i++)
  11. for (int j = 1; j <= 2020; j++)
  12. {
  13. if (gcd(i, j) == 1) res++;
  14. }
  15. cout << res << endl;//答案为:2481215
  16. return 0;
  17. }

试题C 蛇形填数(数学)

问题: 根据此规律,推断第20行20列的数字是多少,其中第2行第2列的数字为5

 思路解析:

(1)对于奇数行有:比如说数字‘4’的那一行,则有:从它向它的右上角方向依次+1

(2)对于偶数行有:比如说数字‘10’的那一行,则有:从它向它的右上角方向依次-1

那么同理:

(3)对于奇数列有:比如说数字‘6’的那一列,则有:从它向它的左下角方向依次-1

(4)对于偶数列有:比如说数字‘7’的那一列,则有:从它向它的左下角方向依次+1

那么就有:

定义一个计数器cnt=1,循环for一次,同时枚举(i)行数和列数,如果这个(i)为奇数,那么就让它对应(1)情况,让它向右上角方向++;同理如果这个(i)为偶数,那么就让它对应(4)情况,让它向它的左下角方向++,其中是将cnt的值赋给这个二维数组,并++,最后输出对应行数和列数即可

  1. #include<iostream>
  2. using namespace std;
  3. const int N = 110;
  4. int a[N][N];
  5. int main()
  6. {
  7. int cnt = 1;
  8. int x, y;
  9. for (int i = 1; i <= N; i++)
  10. {
  11. if (i % 2 == 1)
  12. {
  13. for (x = i, y = 1; x >= 1 && y <= i; x--, y++)
  14. {
  15. a[x][y] = cnt++;
  16. }
  17. }
  18. else
  19. {
  20. for (x = 1, y = i; x <= i && y >= 1; x++, y--)
  21. {
  22. a[x][y] = cnt++;
  23. }
  24. }
  25. }
  26. cout << a[20][20] << endl;
  27. return 0;
  28. }

试题D 跑步锻炼(模拟) 

问题:

 思路解析:

最外层循环枚举年份,接着二判断是否是闰年,如果是闰年,那么就让原先定义好的二月天数改为29,接着内循环枚举月份,接着内循环枚举月份中的天数,如果天数是1,或者为周一,那么就让sum+=2,否则则为普通日子:让sum+=1,枚举完一天后,让星期++,并对7取模,注意枚举该年份后,需要将2月的天数重新修改为28

  1. #include<iostream>
  2. using namespace std;
  3. int months[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
  4. int main()
  5. {
  6. int year1 = 2000, year2 = 2020;
  7. int month1 = 1, month2 = 10;
  8. int day1 = 1, day2 = 1;
  9. int week = 6;
  10. long long sum = 0;
  11. for (year1; year1 <= year2; year1++)
  12. {
  13. if ((year1 % 400 == 0) || (year1 % 4 == 0 && year1 % 100 != 0))
  14. {
  15. months[2] = 29;
  16. }
  17. for (int i = 1; i <= 12; i++)
  18. {
  19. for (int day = 1; day <= months[i]; day++)
  20. {
  21. if (day == 1 || week == 1)
  22. {
  23. sum += 2;
  24. }
  25. else
  26. {
  27. sum += 1;
  28. }
  29. week = (week + 1) % 7;
  30. if (year1 == 2020 && i == 10 && day == 1)
  31. {
  32. cout << sum << endl;//答案为:8879
  33. return 0;
  34. }
  35. }
  36. }
  37. months[2] = 28;
  38. }
  39. return 0;
  40. }

试题E 七段码(图论+并查集)

 

 题目概述:

必须要相邻才能发光,也就是所有开着的灯必须是连通的,才能算是一种合法方案,求合法的方案数

  1. #include<iostream>
  2. using namespace std;
  3. int use[10];
  4. int ans, e[10][10], father[10];
  5. void init()
  6. {
  7. //连边建图
  8. //a b c d e f g
  9. //1 2 3 4 5 6 7
  10. e[1][2] = e[1][6] = 1;
  11. e[2][1] = e[2][7] = e[2][3] = 1;
  12. e[3][2] = e[3][4] = e[3][7] = 1;
  13. e[4][3] = e[4][5] = 1;
  14. e[5][4] = e[5][6] = e[5][7] = 1;
  15. e[6][1] = e[6][5] = e[6][7] = 1;
  16. }
  17. int find(int a)
  18. {
  19. //并查集
  20. return (a == father[a]) ? a : father[a] = find(father[a]);
  21. }
  22. void dfs(int d)
  23. {
  24. if (d > 7)//一个七段管的所有灯的状态已经列举完了
  25. {
  26. for (int i = 1; i <= 7; i++)
  27. {
  28. father[i] = i;//初始化
  29. }
  30. for (int i = 1; i <= 7; i++)
  31. {
  32. for (int j = 1; j <= 7; j++)
  33. {//如果存在这条边 且 开i节点 且 开j节点
  34. if (e[i][j] && use[i] && use[j])
  35. {
  36. int fa = find(i), fb = find(j);//寻找i,j对于的祖宗节点
  37. if (fa != fb)//如果祖宗节点不同,证明未连通
  38. {
  39. father[fa] = fb;//连通 a b
  40. }
  41. }
  42. }
  43. }
  44. int k = 0;
  45. for (int i = 1; i <= 7; i++)
  46. {
  47. if (use[i] && i == father[i])//如果它被用过 且 它等于它的祖宗节点
  48. k++;
  49. }
  50. if (k == 1)//只有一个父节点,就说明他们是相连的
  51. ans++;
  52. return;
  53. }
  54. use[d] = 0;//不选
  55. dfs(d + 1);
  56. use[d] = 1; //选
  57. dfs(d + 1);
  58. use[d] = 0;//归位
  59. }
  60. int main()
  61. {
  62. init();
  63. dfs(1);
  64. cout << ans << endl;
  65. return 0;
  66. }

 试题F:成绩统计(格式化输出)

 

格式化输出的陷阱://格式化输出%的陷阱:要输入%%才表示 %

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int n;
  6. cin >> n;
  7. double a = 0;
  8. double b = 0;
  9. int x;
  10. for (int i = 1; i <= n; i++)
  11. {
  12. cin >> x;
  13. if (x >= 60) a++;
  14. if (x >= 85) b++;
  15. }
  16. //格式化输出%的陷阱:要输入%%才表示 %
  17. printf("%.0lf%%\n%.0lf%%", a / (n/100.0), b / (n/100.0));
  18. return 0;
  19. }

 试题G:回文日期(模拟)

 

思路解析:

本题于与Acwing上的回文日期差不多   466. 回文日期 - AcWing题库

通过Acwing的这题,可以得到的经验是:遍历前四位数,然后进行回文构造,然后再判断构造出来的回文串是否满足日期的标准,即判断它是否是合法的日期,即可

先贴一下Acwing上的回文日期的答案:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <algorithm>
  5. using namespace std;
  6. int months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  7. bool check(int date)
  8. {
  9. int year = date / 10000;
  10. int month = date % 10000 / 100;
  11. int day = date % 100;
  12. if (!month || month >= 13 || !day) return false;
  13. if (month != 2 && day > months[month]) return false;
  14. if (month == 2)
  15. {
  16. bool leap = year % 4 == 0 && year % 100 || year % 400 == 0;
  17. if (day > 28 + leap) return false;
  18. }
  19. return true;
  20. }
  21. int main()
  22. {
  23. int date1, date2;
  24. cin >> date1 >> date2;
  25. int res = 0;
  26. for (int i = 1000; i < 10000; i++)
  27. {
  28. int x = i, r = i;//构造后四个:先构造回文串,再判断其是否合法
  29. for (int j = 0; j < 4; j++) r = r * 10 + x % 10, x /= 10;
  30. if (r >= date1 && r <= date2 && check(r)) res++;
  31. }
  32. printf("%d\n", res);
  33. return 0;
  34. }

那么此题与Acwing上不同的地方在于:

输入是一个合法的八位数的日期

第一个输出是,这个日期之后的第一个合法的回文日期

第二个输出是,这个日期之后的第一个符合ABABBABA的日期

注意注释的一些细节即可

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. int months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  5. bool check(int date)//判断是否位合法日期
  6. {
  7. int year = date / 10000;
  8. int month = date % 10000 / 100;
  9. int day = date % 100;
  10. //如果月份为0 || 月份大于等于13 || 日数为0
  11. if (!month || month >= 13 || !day) return false;
  12. //如果月份!=2 && 日数大于该月的天数
  13. if (month != 2 && day > months[month]) return false;
  14. if (month == 2)
  15. {
  16. bool leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
  17. if (day > 28 + leap) return false;//如果二月的天数>大于原本的二月天数+(可能的闰年+1的天数)
  18. }
  19. return true;
  20. }
  21. bool checkAB(int s)//判断AB
  22. {
  23. bool flag = false;
  24. int y, m, d;
  25. y = s / 10000; // 得到年份
  26. m = (s / 100) % 100; // 得到月份 (BA)
  27. d = s % 100; // 得到日 (BA)
  28. int k, n;
  29. k = y / 100; // AB
  30. n = y % 100; // AB
  31. if (k == n && m == d) // 判断是否为ABABBABA
  32. {
  33. flag = true;
  34. }
  35. return flag;
  36. }
  37. int main()
  38. {
  39. int date;
  40. cin >> date;
  41. int flag = 0;
  42. for (int i = (date/10000) +1; i <= 9999; i++)//枚举四位数的年份
  43. { //举例 i=1234
  44. int left = i;
  45. int right = i;//构造出来的回文串,构造出来的回文串即:12344321
  46. for (int j = 0; j < 4; j++)
  47. {
  48. right = right * 10 + left % 10;//1234*10+1234%10
  49. left = left / 10;//得到left的下一位
  50. }//因为是按顺序进行的枚举,所以先符合条件的第一个必然是顺序最小的那一个
  51. if (check(right))
  52. {
  53. flag++;//保证第一个符合的回文串只有一个输出
  54. if (flag == 1) cout << right << endl;
  55. if (checkAB(right))
  56. {
  57. cout << right << endl;
  58. return 0;
  59. }
  60. }
  61. }
  62. }

试题H:子串分值和(哈希+乘法原理+数学)

 (1)暴力思路解析:哈希表存储字母出现的次数,查看哈希表中是否有多个字母即可

最外层循环枚举一次走的步长

        第二层循环枚举左端点

                第三层循环将左端点~~~左端点+步长的这一段距离 赋值

                查找哈希表中是否存在元素,如果存在即 res++

                memset:重新初始化哈希表

cout<<res<<endl;

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<string>
  5. using namespace std;
  6. int main()
  7. {
  8. string s;
  9. cin >> s;
  10. int n = s.size();
  11. int cnt[27] = { 0 };
  12. int res = 0;
  13. for (int len = 1; len <= n; len++)//枚举长度
  14. {
  15. for (int i = 0; i + len <= n; i++)//枚举左端点
  16. {
  17. for (int j = i; j < i + len; j++)//将该段进行填充
  18. {
  19. cnt[s[j] - 'a']++;
  20. }
  21. for (int k = 0; k < 26; k++)//查找
  22. {
  23. if (cnt[k] != 0) res++;
  24. }
  25. memset(cnt, 0, sizeof cnt);//重新赋值为:0
  26. }
  27. }
  28. cout << res << endl;
  29. return 0;
  30. }


 (2)小优化:变成两层循环

  1. #include<iostream>
  2. #include<string>
  3. #include<cstring>
  4. using namespace std;
  5. const int N = 10010;
  6. char str[N];
  7. bool st[26];
  8. int main()
  9. {
  10. scanf("%s", str + 1);
  11. int n = strlen(str + 1);
  12. long long ans = 0;
  13. for (int i = 1; i <= n; i++)
  14. {
  15. memset(st, 0, sizeof st);
  16. int t = str[i] - 'a';
  17. st[t] = true;
  18. int cnt = 1;
  19. ans += cnt;
  20. for (int j = i - 1; j >= 1; j--)
  21. {
  22. int t = str[j] - 'a';
  23. if (!st[i])
  24. {
  25. cnt++;
  26. st[t] = true;
  27. }
  28. ans += cnt;
  29. }
  30. }
  31. printf("%lld\n", ans);
  32. return 0;
  33. }


 (3)大优化

第十一届蓝桥杯 ——子串分值和_六级不考550+不改名-CSDN博客

  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. typedef long long ll;
  5. int last[127];
  6. int main()
  7. {
  8. string s;
  9. cin >> s;
  10. int n = s.size();
  11. s = ' ' + s;
  12. ll ans = 0;
  13. for (int i = 1; i <= n; i++)
  14. {
  15. ans += (ll)(i - last[s[i]]) * (n - i + 1);
  16. last[s[i]] = i;
  17. }
  18. cout << ans << endl;
  19. return 0;
  20. }


试题I:平面切分(数学)

  1. #include<iostream>
  2. #include<set>
  3. using namespace std;
  4. typedef pair<double, double> pdd;
  5. set<pdd> lines;
  6. int res = 1;
  7. int cmp(double c, double d)//求此条直线与之前所有直线的交点的个数
  8. {
  9. set<pdd> points;
  10. pdd it;
  11. for (auto i = lines.begin(); i != lines.end(); i++)
  12. {
  13. double a = i->first;
  14. double b = i->second;
  15. if (a != c)//斜率不同,则两条直线不重合
  16. {
  17. it.first = (d - b) / (a - c);//求交点的横坐标
  18. it.second = c * it.first + d;//纵坐标
  19. points.insert(it);
  20. }
  21. }
  22. return points.size();
  23. }
  24. int main()
  25. {
  26. int n;
  27. cin >> n;
  28. while (n--)
  29. {
  30. double a, b;
  31. cin >> a >> b;
  32. int count1 = lines.size();
  33. lines.insert({ a,b });
  34. if (lines.size() != count1)
  35. {
  36. res++;
  37. res += cmp(a, b);
  38. }
  39. }
  40. cout << res << endl;
  41. return 0;
  42. }

 试题J:字串排序(dp)

在这里插入图片描述

 第十一届蓝桥杯——字串排序(DP)_Dripping Blog-CSDN博客_字串排序

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/361777
推荐阅读
相关标签
  

闽ICP备14008679号