当前位置:   article > 正文

蓝桥杯 第 3 场算法双周赛(E-H) 解题报告 | 珂学家_蓝桥杯之深秋的苹果

蓝桥杯之深秋的苹果

前言


整体评价

这场题变多了,但是难度不如上周,G题是道经典的博弈题,H题是一道有趣的图论题,需要优化(过河拆桥)。

这篇主要写E-H的题解,上一篇写A-D的题解。

蓝桥杯 第 3 场算法双周赛

顺便小小的吐槽下,比赛的机器性能还行,但比赛后的评测机感觉降级了。


E. 深秋的苹果

Q: 求划分为M段后,最大美味值最小?

最大最小一般就是二分解法,不过这题上界比较大, 是 2e18

本质是对公式

 \sum_{i=l}^{r} \sum_{j=i+}^{r} A_i *A_j

 \sum_{i=l}^{r} \sum_{j=i+}^{r} A_i *A_j \Rightarrow (\sum_{i=l}^{r} A_i)^2 / 2

只要能转化为这个式子,那这题基本就稳了

在核心的check逻辑中,从左到右贪心构造满足条件的最大段,然后看段的数量是否<=k

  1. import java.io.BufferedInputStream;
  2. import java.util.Scanner;
  3. import java.util.function.Function;
  4. public class Main {
  5. public static void main(String[] args) {
  6. Scanner sc = new Scanner(new BufferedInputStream(System.in));
  7. int n = sc.nextInt();
  8. int k = sc.nextInt();
  9. long acc = 0;
  10. long[] arr = new long[n];
  11. for (int i = 0; i < n; i++) {
  12. arr[i] = sc.nextLong();
  13. acc += arr[i];
  14. }
  15. // 编写check函数和逻辑
  16. Function<Long, Boolean> check = (m) -> {
  17. int cnt = 0;
  18. long acc1 = 0; // 维护\sum ai
  19. long acc2 = 0; // 维护\sum ai^2
  20. for (int i = 0; i < n; i++) {
  21. acc1 += arr[i];
  22. acc2 += arr[i] * arr[i];
  23. long x = (acc1 * acc1 - acc2) / 2;
  24. if (x > m) {
  25. cnt++;
  26. acc1 = arr[i];
  27. acc2 = arr[i] * arr[i];
  28. }
  29. }
  30. cnt++;
  31. return cnt <= k;
  32. };
  33. long l = 0, r = 2l * acc * acc;
  34. while (l <= r) {
  35. long m = l + (r - l) / 2;
  36. if (check.apply(m)) {
  37. r = m - 1;
  38. } else {
  39. l = m + 1;
  40. }
  41. }
  42. System.out.println(l);
  43. }
  44. }

F. 鲜花之海

Q:  在一个n*n元组对中,找到第k小的元组,排序规则元组(a, b)中,a+b优先,a其次

有多种解

  • 二分
  • 解方程

对于二分的解法,a+b优先,所以采用对角线(y=x+k)作为二分的依据, 这边的长度从1~N,在从N~1,处理起来有点变扭,需要特别注意。

  1. import java.io.BufferedInputStream;
  2. import java.util.Scanner;
  3. public class Main {
  4. public static void main(String[] args) {
  5. Scanner sc = new Scanner(new BufferedInputStream(System.in));
  6. int t = sc.nextInt();
  7. while (t-- > 0) {
  8. int n = sc.nextInt();
  9. long k = sc.nextLong();
  10. int l = 1, r = 2 * n - 1;
  11. while (l <= r) {
  12. int m = l + (r - l) / 2;
  13. long s = 0;
  14. if (m <= n) {
  15. s = (long)(1 + m) * m / 2;
  16. } else {
  17. int e = 2 * n - 1 - m;
  18. s = (long)n * n - (long)(e + 1) * e / 2;
  19. }
  20. if (s < k) {
  21. l = m + 1;
  22. } else {
  23. r = m - 1;
  24. }
  25. }
  26. if (l <= n) {
  27. // 对角线
  28. int row = (int) (k - (long) l * (l - 1) / 2);
  29. int col = l + 1 - row;
  30. System.out.println(row + " " + col);
  31. } else {
  32. int e = 2 * n - 1 - l;
  33. int left = (int)((long)n * n - (long)(e + 1) * e / 2 - k);
  34. // 存在一个截距
  35. int s = l + 1 - n;
  36. int col = s + left;
  37. int row = l + 1 - col;
  38. System.out.println(row + " " + col);
  39. }
  40. }
  41. }
  42. }

G. 斐波拉契跳跃

Q: 在一个数组中,必须按照斐波那契步长跳跃(比前一次更长,且目标位置数大于当前值),谁不能行动,谁就认输?

很经典的博弈题,寻找必胜态和必败态

经典的alpha+beta剪枝解法

数组很长,但是斐波那契数组增长非常快,10^5范围内,只有24项。

如果令 dp[i][s],  i为位子,s为步长,实际只有O(24*n)的状态总量

借助记忆化搜索,可以快速求解出来。

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.util.*;
  5. import java.util.stream.Collectors;
  6. public class Main {
  7. static class Solution {
  8. List<Integer> steps;
  9. int n;
  10. int[] arr;
  11. Map<Long, Boolean> memo = new HashMap<>();
  12. public void ready(int n, int[] arr, List<Integer> steps) {
  13. this.n = n;
  14. this.arr = arr;
  15. this.steps = steps;
  16. }
  17. public boolean dfs(int u, int s) {
  18. long key = (long)u << 32 | s;
  19. if (memo.containsKey(key)) {
  20. return memo.get(key);
  21. }
  22. for (int i = s; i < steps.size(); i++) {
  23. int jump = steps.get(i);
  24. if (u + jump < n && arr[u] < arr[u+ jump]) {
  25. if (!dfs(u + jump, i + 1)) {
  26. memo.put(key, true);
  27. return true;
  28. }
  29. }
  30. if (u - jump >= 0 && arr[u] < arr[u - jump]) {
  31. if (!dfs(u - jump, i + 1)) {
  32. memo.put(key, true);
  33. return true;
  34. }
  35. }
  36. }
  37. memo.put(key, false);
  38. return false;
  39. }
  40. }
  41. public static void main(String[] args) {
  42. AReader sc = new AReader();
  43. int n = sc.nextInt();
  44. int[] arr = new int[n];
  45. for (int i = 0; i < n; i++) {
  46. arr[i] = sc.nextInt();
  47. }
  48. // 预处理
  49. List<Integer> steps = new ArrayList<>();
  50. int f0 = 1, f1 = 1;
  51. while (f1 < n) {
  52. steps.add(f1);
  53. int t = f1 + f0;
  54. f0 = f1;
  55. f1 = t;
  56. }
  57. List<String> result = new ArrayList<>();
  58. Solution solution = new Solution();
  59. solution.ready(n, arr, steps);
  60. for (int i = 0; i < n; i++) {
  61. boolean r = solution.dfs(i, 0);
  62. result.add(r ? "Little Lan" : "Little Qiao");
  63. }
  64. System.out.println(result.stream().collect(Collectors.joining("\n")));
  65. }
  66. static
  67. class AReader {
  68. private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  69. private StringTokenizer tokenizer = new StringTokenizer("");
  70. private String innerNextLine() {
  71. try {
  72. return reader.readLine();
  73. } catch (IOException ex) {
  74. return null;
  75. }
  76. }
  77. public boolean hasNext() {
  78. while (!tokenizer.hasMoreTokens()) {
  79. String nextLine = innerNextLine();
  80. if (nextLine == null) {
  81. return false;
  82. }
  83. tokenizer = new StringTokenizer(nextLine);
  84. }
  85. return true;
  86. }
  87. public String nextLine() {
  88. tokenizer = new StringTokenizer("");
  89. return innerNextLine();
  90. }
  91. public String next() {
  92. hasNext();
  93. return tokenizer.nextToken();
  94. }
  95. public int nextInt() {
  96. return Integer.parseInt(next());
  97. }
  98. public long nextLong() {
  99. return Long.parseLong(next());
  100. }
  101. // public BigInteger nextBigInt() {
  102. // return new BigInteger(next());
  103. // }
  104. // 若需要nextDouble等方法,请自行调用Double.parseDouble包装
  105. }
  106. }


H. 星石传送阵

Q: 给了一个序列数组,但是按照规则可以构建边(双向边)形成一个稠密图,求A点到B点的最小距离。

这题难点在于

稠密图

拿最简单的BFS求路径来说,其时间复杂度为\sum v_i * e_i, V是顶点数,E是点V的均摊边数,这题容易TLE,本质的原因就在这里。假设E=V, 那就是O(V^2)

那如何求解这题呢?

这边有个概念,叫做组(同一类)

可以按组去存放这些点集列表,因为BFS的特性

里面的点集必然,最多相差1

如果从一个点出发,已经通过这个组(点集)遍历了其他节点。那么其他点(同属组),在遍历这个组,就没必要了,因为没有增益优化的空间了。可以细细评味这个。

所以把这个技巧,称之为

过河拆桥

至于质因子拆解,这边就不展开讲了。

这题题意还是有点绕,比赛的时候,看了很久,都懵圈了。

  1. import java.io.BufferedInputStream;
  2. import java.util.*;
  3. public class Main {
  4. static List<Integer> primes = new ArrayList<>();
  5. static {
  6. int mz = 10000;
  7. boolean[] vis = new boolean[mz + 1];
  8. for (int i = 2; i <= mz; i++) {
  9. if (!vis[i]) {
  10. primes.add(i);
  11. for (int j = i * i; j <= mz; j+=i) {
  12. vis[j] = true;
  13. }
  14. }
  15. }
  16. }
  17. static int solve(int v) {
  18. int res = 0;
  19. for (int i = 0; i < primes.size(); i++) {
  20. int t = primes.get(i);
  21. if (v < t) break;
  22. if (v % t == 0) {
  23. while (v % t == 0) {
  24. res += t;
  25. v /= t;
  26. }
  27. }
  28. }
  29. if (v > 1) {
  30. res += v;
  31. }
  32. return res;
  33. }
  34. public static void main(String[] args) {
  35. Scanner sc = new Scanner(new BufferedInputStream(System.in));
  36. int n = sc.nextInt();
  37. int a = sc.nextInt(), b = sc.nextInt();
  38. List<Integer> []g = new List[n + 1];
  39. Arrays.setAll(g, x -> new ArrayList<>());
  40. int[] values = new int[n + 1];
  41. int[] arr = new int[n + 1];
  42. Map<Integer, List<Integer>> hash = new HashMap<>();
  43. for (int i = 1; i <= n; i++) {
  44. arr[i] = sc.nextInt();
  45. values[i] = solve(arr[i]) % n + 1;
  46. hash.computeIfAbsent(values[i], x -> new ArrayList<>()).add(i);
  47. if (values[i] >= 1 && values[i] <= n && values[i] != i) {
  48. g[i].add(values[i]);
  49. g[values[i]].add(i);
  50. }
  51. }
  52. int inf = Integer.MAX_VALUE / 10;
  53. int[] res = new int[n + 1];
  54. Arrays.fill(res, inf);
  55. Deque<Integer> deq = new ArrayDeque<>();
  56. deq.offer(a);
  57. res[a] = 0;
  58. while (!deq.isEmpty()) {
  59. int u = deq.poll();
  60. int w = values[u];
  61. List<Integer> edges = hash.getOrDefault(w, Collections.EMPTY_LIST);
  62. for (int v : edges) {
  63. if (v == u) continue;
  64. if (res[v] > res[u] + 1) {
  65. res[v] = res[u] + 1;
  66. deq.offer(v);
  67. }
  68. }
  69. hash.remove(w);
  70. for (int v: g[u]) {
  71. if (v == u) continue;
  72. if (res[v] > res[u] + 1) {
  73. res[v] = res[u] + 1;
  74. deq.offer(v);
  75. }
  76. }
  77. }
  78. System.out.println(res[b] == inf ? -1 : res[b]);
  79. }
  80. }

写在最后

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

闽ICP备14008679号