当前位置:   article > 正文

蓝桥杯 2023 年省赛 C++ 组 B 组填空题(日期统计与 01 串的熵)题解_蓝桥杯日期统计

蓝桥杯日期统计

C++ b 组:

A 题:

答案:235

思路:

暴力搜索题,但是要注意剪枝。 2100 爆搜一年都跑不完。

首先年 2023 是确定的,考虑用 a 、 b 、 c 、 d 分别枚举 2、0、2、3。 a 不是 2 就不继续枚举 b , b 不是 0 就不继续枚举 c , d 同理。消去指数级别的复杂度。

用 e 、 f 分别枚举月的十位与个位, e 为 0 时 f 不能取 0, e 为 1 时 f \leq 2 。

用 g 、 h 分别枚举日的十位与个位,注意 g 为 0 时 f 不能取 0,其他时候不用特判,因为循环最后一层不会造成时间复杂度的变化。

然后就可以打出一个月份对应的日期表,注意 2023 年为平年。之后检查一下日期是否合法,如合法就可以把日期从数字拼接成字符串,使用 unordered_set 维护日期是否出现,常数会更好看一点。(因为这些数字范围都很紧凑)

完整代码如下:

  1. //
  2. // main.cpp
  3. // 日期统计
  4. //
  5. // Created by SkyWave Sun on 2023/4/8.
  6. //
  7. #include <iostream>
  8. #include <unordered_set>
  9. using namespace std;
  10. const int nums[] = {0, 5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7, 5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9, 2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3, 8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6, 1, 4, 0, 1,
  11. 0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3};
  12. const int n = 100;
  13. const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  14. unordered_set<string> st;
  15. int main(int argc, const char * argv[]) {
  16. int sum = 0;
  17. for (int a = 1; a <= n; ++a) {//year
  18. if (nums[a] == 2) {
  19. for (int b = a + 1; b <= n; ++b) {//year
  20. if (nums[b] == 0) {
  21. for (int c = b + 1; c <= n; ++c) {//year
  22. if (nums[c] == 2) {
  23. for (int d = c + 1; d <= n; ++d) {//year
  24. if (nums[d] == 3) {
  25. for (int e = d + 1; e <= n; ++e) {//month
  26. if (nums[e] == 0 || nums[e] == 1) {
  27. for (int f = e + 1; f <= n; ++f) {//month
  28. if ((nums[e] == 0 && nums[f] != 0) || (nums[e] == 1 && nums[f] <= 2)) {
  29. for (int g = f + 1; g <= n; ++g) {//day
  30. if (nums[g] <= 3) {
  31. for (int h = g + 1; h <= n; ++h) {//day
  32. if (nums[g] == 0 && nums[h] == 0) {
  33. continue;
  34. }
  35. string str = "2023";
  36. int month = nums[e] * 10 + nums[f];
  37. int day = nums[g] * 10 + nums[h];
  38. str += to_string(nums[e]) + to_string(nums[f]);
  39. str += to_string(nums[g]) + to_string(nums[h]);
  40. if (day <= days[month]) {
  41. if (st.count(str)) {
  42. continue;
  43. }
  44. st.insert(str);
  45. puts(str.c_str());
  46. ++sum;
  47. }
  48. }
  49. }
  50. }
  51. }
  52. }
  53. }
  54. }
  55. }
  56. }
  57. }
  58. }
  59. }
  60. }
  61. }
  62. }
  63. printf("%d\n",sum);
  64. return 0;
  65. }

时间复杂度为小常数 O(n^4)

B 题:

答案:11027421

思路:朴素的暴力枚举 0 的个数(时间复杂度 O(n)​​​​​​​ )\times​​​​​​​ 暴力计算贡献(时间复杂度 O(n) )肯定吃不消。枚举 0 的个数的线性不可避免,考虑优化式子,发现 01 在串里的顺序不影响最终贡献,因为贡献的计算中只有 0 和 1 的数量的参与。于是可以把循环优化成乘法,记 0 在串中数量为 sum0 ,1 在串中数量为 sum1 ,其他参数与原式相同,则式子变为sum1 \times p(1) \times \log_2{p1} + sum0 \times p(0) \times \log_2{p(0)} ,于是可以线性求得答案。

又观察到信息熵大概是长度的一半少一点,于是可以凭感觉从长度的一半往前枚举。注意,因为浮点数是不能等于的,并且在此题中因无限循环小数参与过多,导致误差会相差很大,所以建议将浮点数的偏移量设置成 10^{-4}

代码:

  1. //
  2. // main.cpp
  3. // 01 串的熵
  4. //
  5. // Created by SkyWave Sun on 2023/4/8.
  6. //
  7. #include <iostream>
  8. #include <cmath>
  9. using namespace std;
  10. const double eps = 1e-4;
  11. const double ans = 11625907.5798;
  12. const int n = 23333333;
  13. double f(int sum1, int sum0) {
  14. double ans = 0.0;
  15. double p1 = (double)sum1 / (double)n;
  16. double p0 = (double)sum0 / (double)n;
  17. ans += sum1 * p1 * log2(p1);
  18. ans += sum0 * p0 * log2(p0);
  19. return ans;
  20. }
  21. int main(int argc, const char * argv[]) {
  22. for (int i = n >> 1; i >= 0; --i) {
  23. double tmp = -f(n - i, i);
  24. if (fabs(tmp - ans) <= eps) {
  25. printf("%d\n",i);
  26. break;
  27. }
  28. }
  29. return 0;
  30. }

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

闽ICP备14008679号