image.png
虽然排名不高,但考虑到t3和t4都是原题,而我都是现场做的,且没有wrong,就没那么难受了

6148. 矩阵中的局部最大值

给你一个大小为 n x n 的整数矩阵 grid 。
生成一个大小为 (n - 2) x (n - 2) 的整数矩阵 maxLocal ,并满足:

  • maxLocal[i][j] 等于 grid 中以 i + 1 行和 j + 1 列为中心的 3 x 3 矩阵中的 最大值

换句话说,我们希望找出 grid 中每个 3 x 3 矩阵中的最大值。
返回生成的矩阵。

示例 1:
image.png
输入:grid = [[9,9,8,1],[5,6,2,6],[8,2,6,4],[6,2,2,2]] 输出:[[9,9],[8,6]] 解释:原矩阵和生成的矩阵如上图所示。 注意,生成的矩阵中,每个值都对应 grid 中一个相接的 3 x 3 矩阵的最大值。
示例 2:
image.png
输入:grid = [[1,1,1,1,1],[1,1,1,1,1],[1,1,2,1,1],[1,1,1,1,1],[1,1,1,1,1]] 输出:[[2,2,2],[2,2,2],[2,2,2]] 解释:注意,2 包含在 grid 中每个 3 x 3 的矩阵中。

提示:

  • n == grid.length == grid[i].length
  • 3 <= n <= 100
  • 1 <= grid[i][j] <= 100

思路:
暴力枚举即可

  1. class Solution {
  2. public int[][] largestLocal(int[][] g) {
  3. int n = g.length;
  4. int[][] res = new int[n - 2][n - 2];
  5. for (int i = 0; i < n - 2; i++) {
  6. for (int j = 0; j < n - 2; j++) {
  7. int max = 0;
  8. for (int l = i; l <= i + 2; l++) {
  9. for (int r = j; r <= j + 2; r++) {
  10. max = Math.max(max, g[l][r]);
  11. }
  12. }
  13. res[i][j] = max;
  14. }
  15. }
  16. return res;
  17. }
  18. }

进阶问题:如果不是求33矩阵的最大值,而是hw怎么办?
答:用单调队列优化,先行再列
Acwing 1091. 理想的正方形

6149. 边积分最高的节点

显示英文描述
我的提交返回竞赛

  • 通过的用户数4810
  • 尝试过的用户数5430
  • 用户总通过次数4868
  • 用户总提交次数12955
  • 题目难度Medium

image.pngimage.png
给你一个有向图,图中有 n 个节点,节点编号从 0 到 n - 1 ,其中每个节点都 恰有一条 出边。
图由一个下标从 0 开始、长度为 n 的整数数组 edges 表示,其中 edges[i] 表示存在一条从节点 i 到节点 edges[i] 的 有向 边。
节点 i 的 边积分 定义为:所有存在一条指向节点 i 的边的节点的 编号 总和。
返回 边积分 最高的节点。如果多个节点的 边积分 相同,返回编号 最小 的那个。

示例 1:
输入:edges = [1,0,0,0,0,7,7,5] 输出:7 解释: - 节点 1、2、3 和 4 都有指向节点 0 的边,节点 0 的边积分等于 1 + 2 + 3 + 4 = 10 。 - 节点 0 有一条指向节点 1 的边,节点 1 的边积分等于 0 。 - 节点 7 有一条指向节点 5 的边,节点 5 的边积分等于 7 。 - 节点 5 和 6 都有指向节点 7 的边,节点 7 的边积分等于 5 + 6 = 11 。 节点 7 的边积分最高,所以返回 7 。
示例 2:
输入:edges = [2,0,0,2] 输出:0 解释: - 节点 1 和 2 都有指向节点 0 的边,节点 0 的边积分等于 1 + 2 = 3 。 - 节点 0 和 3 都有指向节点 2 的边,节点 2 的边积分等于 0 + 3 = 3 。 节点 0 和 2 的边积分都是 3 。由于节点 0 的编号更小,返回 0 。

提示:

  • n == edges.length
  • 2 <= n <= 105
  • 0 <= edges[i] < n
  • edges[i] != i

思路:
统计每个节点的入边权值和,找最大且编号最小的点即可

  1. class Solution {
  2. public int edgeScore(int[] e) {
  3. int n = e.length;
  4. long[] c = new long[n];
  5. for (int i = 0; i < n; i++) {
  6. c[e[i]] += i;
  7. }
  8. int max = 0;
  9. for (int i = 0; i < n; i++) {
  10. if (c[i] > c[max])
  11. max = i;
  12. }
  13. return max;
  14. }
  15. }

6150. 根据模式串构造最小数字

显示英文描述
我的提交返回竞赛

  • 通过的用户数3131
  • 尝试过的用户数3555
  • 用户总通过次数3261
  • 用户总提交次数5251
  • 题目难度Medium

给你下标从 0 开始、长度为 n 的字符串 pattern ,它包含两种字符,’I’ 表示 上升 ,’D’ 表示 下降
你需要构造一个下标从 0 开始长度为 n + 1 的字符串,且它要满足以下条件:

  • num 包含数字 ‘1’ 到 ‘9’ ,其中每个数字 至多 使用一次。
  • 如果 pattern[i] == ‘I’ ,那么 num[i] < num[i + 1] 。
  • 如果 pattern[i] == ‘D’ ,那么 num[i] > num[i + 1] 。

请你返回满足上述条件字典序 最小 的字符串_ _num。

示例 1:
输入:pattern = “IIIDIDDD” 输出:“123549876” 解释: 下标 0 ,1 ,2 和 4 处,我们需要使 num[i] < num[i+1] 。 下标 3 ,5 ,6 和 7 处,我们需要使 num[i] > num[i+1] 。 一些可能的 num 的值为 “245639871” ,”135749862” 和 “123849765” 。 “123549876” 是满足条件最小的数字。 注意,”123414321” 不是可行解因为数字 ‘1’ 使用次数超过 1 次。
示例 2:
输入:pattern = “DDD” 输出:“4321” 解释: 一些可能的 num 的值为 “9876” ,”7321” 和 “8742” 。 “4321” 是满足条件最小的数字。

提示:

  • 1 <= pattern.length <= 8
  • pattern 只包含字符 ‘I’ 和 ‘D’ 。

思路:
方法1:看到数据比较小,直接爆搜

  1. // 赛时代码
  2. class Solution {
  3. String p;
  4. int n;
  5. int[] path = new int[10];
  6. boolean[] st = new boolean[10];
  7. String res = null;
  8. public String smallestNumber(String pattern) {
  9. p = pattern;
  10. n = p.length() + 1;
  11. dfs(0);
  12. return res;
  13. }
  14. boolean dfs(int u) {
  15. if (u == n) {
  16. if (check()) {
  17. StringBuilder sb = new StringBuilder();
  18. for (int i = 0; i < n; i++)
  19. sb.append(path[i]);
  20. res = sb.toString();
  21. return true;
  22. }
  23. return false;
  24. }
  25. for (int i = 1; i <= 9; i ++) {
  26. if (st[i]) continue;
  27. st[i] = true;
  28. path[u] = i;
  29. if (dfs(u + 1)) return true;
  30. st[i] = false;
  31. }
  32. return false;
  33. }
  34. boolean check() {
  35. for (int i = 0; i < n - 1; i++) {
  36. char c = p.charAt(i);
  37. if (c == 'I') {
  38. if (path[i + 1] < path[i]) return false;
  39. } else {
  40. if (path[i + 1] > path[i]) return false;
  41. }
  42. }
  43. return true;
  44. }
  45. }
  46. // 优化一下,不是最后check,而是边填边check
  47. class Solution {
  48. String res = null;
  49. int n;
  50. String p;
  51. public String smallestNumber(String pattern) {
  52. this.n = pattern.length();
  53. this.p = pattern;
  54. for (int i = 1; i <= n + 1; i++) {
  55. if (dfs(0, i, 1 << i, i))
  56. break;
  57. }
  58. return res;
  59. }
  60. boolean dfs(int u, int pre, int st, int x) {
  61. if (u == n) {
  62. res = x + "";
  63. return true;
  64. }
  65. char c = p.charAt(u);
  66. for (int i = 1; i <= n + 1; i++) {
  67. if ((st >> i & 1) == 1) continue;
  68. if (c == 'I' && i < pre) continue;
  69. if (c == 'D' && i > pre) continue;
  70. if (dfs(u + 1, i, st | 1 << i, x * 10 + i)) return true;
  71. }
  72. return false;
  73. }
  74. }

方法2:用栈构造
先考虑最特殊的情况III...III全为I
再考虑中间有D怎么办
IIIII = 1 2 3 4 5
IIDII= 1 2 4 3 5

  1. class Solution {
  2. public String smallestNumber(String p) {
  3. int n = p.length();
  4. char[] a = new char[n + 1];
  5. int idx = 0;
  6. Deque<Character> stk = new ArrayDeque<>();
  7. for (int i = 1; i <= n; i++) {
  8. if (p.charAt(i - 1) == 'I') {
  9. stk.push((char)(i + '0'));
  10. while (!stk.isEmpty())
  11. a[idx++] = stk.pop();
  12. } else {
  13. stk.push((char)(i + '0'));
  14. }
  15. }
  16. stk.push((char)(n + 1 + '0'));
  17. while (!stk.isEmpty())
  18. a[idx++] = stk.pop();
  19. return new String(a);
  20. }
  21. }

原题:Leetcode 484. 寻找排列
扩展:903. DI 序列的有效排列

6151. 统计特殊整数

如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数
给你一个 整数 n ,请你返回区间 [1, n] 之间特殊整数的数目。

示例 1:
输入:n = 20 输出:19 解释:1 到 20 之间所有整数除了 11 以外都是特殊整数。所以总共有 19 个特殊整数。
示例 2:
输入:n = 5 输出:5 解释:1 到 5 所有整数都是特殊整数。
示例 3:
输入:n = 135 输出:110 解释:从 1 到 135 总共有 110 个整数是特殊整数。 不特殊的部分数字为:22 ,114 和 131 。

提示:

  • 1 <= n <= 2 * 109

思路:方法1:数位DP

  1. class Solution {
  2. public int countSpecialNumbers(int n) {
  3. if (n <= 10) return n;
  4. boolean[] st = new boolean[10];
  5. String num = Integer.toString(n);
  6. n = num.length();
  7. int res = 0;
  8. for (int i = 0; i < n; i++) {
  9. int x = num.charAt(i) - '0';
  10. int c = 0;
  11. for (int j = 0; j < x; j++)
  12. if (!st[j]) c++;
  13. if (i == 0) {
  14. // 0
  15. for (int j = 0; j < n - 1; j++) {
  16. res += 9 * A(9, j);
  17. }
  18. // 非0
  19. c--;
  20. if (c >= 0)
  21. res += c * A(9, n - 1);
  22. } else {
  23. res += c * A(10 - (i + 1), n - 1 - i);
  24. }
  25. if (st[x]) break;
  26. st[x] = true;
  27. if (i == n - 1){
  28. res++;
  29. break;
  30. }
  31. }
  32. return res;
  33. }
  34. int A(int x, int y) {
  35. int res = 1;
  36. for (int i = y; i > 0; i--)
  37. res *= x--;
  38. return res;
  39. }
  40. }
  1. // 记忆化搜索
  2. class Solution {
  3. int[][] f = new int[10][1 << 10];
  4. List<Integer> num = new ArrayList<>();
  5. public int countSpecialNumbers(int n) {
  6. while (n > 0) {
  7. num.add(n % 10);
  8. n /= 10;
  9. }
  10. for (int i = 0; i < 10; i++)
  11. Arrays.fill(f[i], -1);
  12. return dfs(num.size() - 1, 1, 1, 0);
  13. }
  14. int dfs(int cur, int lim, int lead, int mask) {
  15. if (cur == -1)
  16. return lead == 0 ? 1 : 0;
  17. int ans = f[cur][mask];
  18. if (lim != 1 && lead != 1 && ans >= 0) return ans;
  19. ans = 0;
  20. if (lead == 1) ans += dfs(cur - 1, 0, 1, mask);
  21. for (int x = lead == 1 ? 1 : 0; x <= (lim == 1 ? num.get(cur) : 9); x++) {
  22. if ((mask >> x & 1) == 1) continue;
  23. if (lim == 1 && x == num.get(cur))
  24. ans += dfs(cur - 1, 1, 0, mask | (1 << x));
  25. else
  26. ans += dfs(cur - 1, 0, 0, mask | (1 << x));
  27. }
  28. if (lead != 1 && lim != 1) f[cur][mask] = ans;
  29. return ans;
  30. }
  31. }

方法2:爆搜
需要加点技巧(lowbit),减少无效枚举

  1. class Solution {
  2. int[] a = new int[1024];
  3. int n;
  4. int res;
  5. public int countSpecialNumbers(int n) {
  6. this.n = n;
  7. for (int i = 0; i < 10; i++)
  8. a[1 << i] = i;
  9. for (int i = 1; i < 10; i++) {
  10. dfs(i, 1023 - (1 << i));
  11. }
  12. return res;
  13. }
  14. void dfs(long x, int st) {
  15. if (x > n) return;
  16. res++;
  17. int cur = 0;
  18. while (st > 0) {
  19. int v = st & (-st);
  20. st -= v;
  21. dfs(x * 10 + a[v], st + cur);
  22. cur += v;
  23. }
  24. }
  25. }

原题:1012. 至少有 1 位重复的数字