https://leetcode.cn/contest/tianchi2022/

image.png
菜菜

221021天池-01. 统计链表奇数节点

给你一个链表的头节点 head,请统计链表中值为 奇数 的节点个数
示例 1:
输入:head = [2,1,8] 输出:1 解释:链表中存在 1 个奇数值的节点,值为 1
示例 2:
输入:head = [1,2,3,4] 输出:2 解释:链表中存在 2 个奇数值的节点,值分别为 1、3
提示:

  • 链表中节点的数目在 [1, 5000] 范围内。
  • 1 <= Node.val <= 10000

思路:模拟

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode() {}
  7. * ListNode(int val) { this.val = val; }
  8. * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  9. * }
  10. */
  11. class Solution {
  12. public int numberEvenListNode(ListNode head) {
  13. int c = 0;
  14. while (head != null) {
  15. if (head.val % 2 == 1)
  16. c++;
  17. head = head.next;
  18. }
  19. return c;
  20. }
  21. }

221021天池-02. 光线反射

工程师正在研究一个 N*M 大小的光线反射装置,装置内部的构造记录于 grid 中,其中

  • ‘.’ 表示空白区域,不改变光的传播方向
  • ‘R’ 表示向右倾斜的 双面 均可反射光线的镜子,改变光的传播方向
  • ‘L’ 表示向左倾斜的 双面 均可反射光线的镜子,改变光的传播方向

假如光线从装置的左上方垂直向下进入装置,请问在离开装置前,光线在装置内部经过多长的路线。

示例 1:
输入:grid = [“…”,”L.L”,”RR.”,”L.R”] 输出:12 解释:如图所示,光线经过路线长度为 12
示例 2:
输入:grid = [“R.”,”..”] 输出:1 解释:如图所示,光线经过路线长度为 1
提示:

  • 1 <= grid.length, grid[i].length <= 100
  • grid[i][j] 仅为 ‘L’、’R’ 和 ‘.’

思路:模拟

  1. class Solution {
  2. public int getLength(String[] grid) {
  3. return dfs(grid, 0, 0, 0);
  4. }
  5. // 0, 1, 2, 3
  6. int[] dx = {1, 0, -1, 0}, dy = {0, -1, 0, 1};
  7. int[] dr = {1, 0, 3, 2}, dl = {3, 2, 1, 0};
  8. int dfs(String[] grid, int from, int x, int y) {
  9. if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length())
  10. return 0;
  11. int res = 1;
  12. char c = grid[x].charAt(y);
  13. if (c == '.') {
  14. res += dfs(grid, from, x + dx[from], y + dy[from]);
  15. } else if (c == 'R') {
  16. from = dr[from];
  17. res += dfs(grid, from, x + dx[from], y + dy[from]);
  18. } else {
  19. from = dl[from];
  20. res += dfs(grid, from, x + dx[from], y + dy[from]);
  21. }
  22. return res;
  23. }
  24. }

221021天池-03. 整理书架

书架上有若干本书,从左至右的书籍编号记于整型数组 order 中。为保证书籍多样性,管理员想取走一些重复编号的书籍,要求满足以下条件:

  • 剩余书本中相同编号的书本数量均不大于 limit
  • 取走的书籍数量尽可能少

由于存在多种整理方案,请返回剩余书本编号的排列为「最小排列」的方案。
注意:

  • 「最小排列」:若干数组中第一位数字最小的数组;若第一位数字相同,则为第二位数字最小的数组,以此类推。

示例 1:
输入:order = [5,5,6,5], limit = 2 输出:[5,5,6] 解释:order 中出现了 3 次 5 号书: 方案 1:去掉 order[0] 或 order[1],所得排列为 [5,6,5]; 方案 2:去掉 order[3],所得排列为 [5,5,6]; 经比较,最终返回排列最小的方案 2:[5,5,6]。
示例 2:
输入:order = [5,5,6,5], limit = 3 输出:[5,5,6,5] 解释:order 中所有相同编号的书本数目均未超过 limit,不需要去除,返回 [5,5,6,5]
示例 3:
输入:order = [3,3,9,8,9,2,8], limit = 1 输出:[3,8,9,2] 解释:列表中 3、8、9 号数字都出现了 2 次,去掉编号相同的书后的排列结果中 [3,8,9,2] 为排列最小的结果。
示例 4:
输入:order = [2,1,2,2,1,3,3,1,3,3], limit = 2 输出:[1,2,2,1,3,3]
提示:

  • 1 <= order.length <= 10^5
  • 1 <= limit <= 10
  • 1 <= order[i] <= 10^6

思路:贪心 + 单调栈

  1. class Solution {
  2. public int[] arrangeBookshelf(int[] a, int limit) {
  3. int n = a.length;
  4. int[] stk = new int[n];
  5. int p = -1;
  6. Map<Integer, Integer> map = new HashMap<>();
  7. Map<Integer, Integer> cnt = new HashMap<>();
  8. Map<Integer, Integer> used = new HashMap<>();
  9. for (int x : a) map.merge(x, 1, Integer::sum);
  10. for (int i = 0; i < n; i++) {
  11. if (cnt.getOrDefault(a[i], 0) >= limit) {
  12. used.merge(a[i], 1, Integer::sum);
  13. continue;
  14. }
  15. while (p > -1 && a[i] < a[stk[p]] && cnt.get(a[stk[p]]) - 1 + map.get(a[stk[p]]) - used.get(a[stk[p]]) >= limit) {
  16. int idx = stk[p--];
  17. cnt.merge(a[idx], -1, Integer::sum);
  18. }
  19. used.merge(a[i], 1, Integer::sum);
  20. stk[++p] = i;
  21. cnt.merge(a[i], 1, Integer::sum);
  22. }
  23. int[] res = new int[p + 1];
  24. for (int i = 0; i <= p; i++)
  25. res[i] = a[stk[i]];
  26. return res;
  27. }
  28. }

类似的题目:
2434. 使用机器人打印字典序最小的字符串
2030. 含特定字母的最小子序列

  1. class Solution {
  2. public String smallestSubsequence(String s, int k, char letter, int repetition) {
  3. int n = s.length();
  4. char[] c = s.toCharArray();
  5. int[] rt = new int[n], r = new int[n];
  6. for (int i = n - 1, a = 0, b = 0; i >= 0; i--) {
  7. rt[i] = a;
  8. r[i] = b;
  9. if (c[i] == letter) a++;
  10. else b++;
  11. }
  12. char[] stk = new char[n];
  13. int p = -1;
  14. for (int i = 0, cnt = 0; i < n; i++) {
  15. while (p > -1 && stk[p] > c[i]) {
  16. if (stk[p] == letter) {
  17. if (cnt + rt[i] - 1 >= repetition && p + r[i] + rt[i] + 1 >= k) {
  18. p--;
  19. cnt--;
  20. } else break;
  21. } else if (p + r[i] + rt[i] + 1 >= k)
  22. p--;
  23. else break;
  24. }
  25. stk[++p] = c[i];
  26. if (c[i] == letter) cnt++;
  27. }
  28. StringBuilder sb = new StringBuilder();
  29. for (int i = 0, cnt = 0; i <= p && sb.length() != k; i++) {
  30. int need = repetition - cnt;
  31. if (stk[i] == letter) {
  32. sb.append(stk[i]);
  33. cnt++;
  34. } else {
  35. if (need < k - sb.length())
  36. sb.append(stk[i]);
  37. }
  38. }
  39. return sb.toString();
  40. }
  41. }

221021天池-04. 意外惊喜

某电商平台举办了一个用户抽奖活动,奖池中共有若干个礼包,每个礼包中包含一些礼物。 present[i][j] 表示第 i 个礼包第 j 件礼(下标从 0 开始)物的价值。抽奖规则如下:

  • 每个礼包中的礼物摆放是有顺序的,你必须从第 0 件礼物开始打开;
  • 对于同一个礼包中的礼物,必须在打开该礼包的第 i 个礼物之后,才能打开第 i+1 个礼物;
  • 每个礼物包中的礼物价值 非严格递增

参加活动的用户总共可以打开礼物 limit 次,请返回用户能够获得的 最大 礼物价值总和。

示例 1:
输入: present = [[1,2],[2,3],[3,4]], limit = 3 输出: 9 解释:最佳的方案为: 第 1 次拿走第 3 个礼包中的第 1 个礼物,得到价值 3; 第 2 次拿走第 3 个礼包中的第 2 个礼物,得到价值 4; 第 3 次拿走第 2 个礼物包的第 1 个礼物,得到价值 2; 返回打开的礼物价值总和 3 + 4 + 2 = 9
示例 2:
输入: present = [[1,2,100],[4,5],[3,4]], limit = 4 输出: 107 解释:最佳的方案为: 第 1 次拿走第 1 个礼包中的第 1 个礼物,得到价值 1; 第 2 次拿走第 1 个礼包中的第 2 个礼物,得到价值 2; 第 3 次拿走第 1 个礼物包的第 3 个礼物,得到价值 100; 第 4 次拿走第 2 个礼物包的第 1 个礼物,得到价值 4; 返回打开的礼物价值总和 107
提示:

  • 1 <= present.length <= 2000
  • 1 <= present[i].length <= 1000
  • 1 <= present[i][j] <= present[i][j+1] <= 10^5
  • 1 <= limit <= 1000

思路:背包 + 分治 + 贪心
CF原题https://codeforces.com/problemset/problem/1442/D