From 5d36b275cae8ba376b5fc14ed5985e9375b9a63d Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Wed, 10 May 2023 23:02:25 +0800 Subject: [PATCH] modify code --- .../Code01_ImageArrayAverageGotoHalf.java | 193 ++++++++++++++++++ .../Code02_LexicographicBiggerSumOfR.java | 180 ++++++++++++++++ .../Code03_SmallestSufficientTeam.java | 140 +++++++++++++ .../Code04_PartitionToKEqualSumSubsets.java | 94 +++++++++ .../Code05_MinReverseOperations.java | 68 ++++++ ...养的大厂算法面试题(正在直播) | 69 +++++++ 6 files changed, 744 insertions(+) create mode 100644 算法周更班/class_2023_05_2_week/Code01_ImageArrayAverageGotoHalf.java create mode 100644 算法周更班/class_2023_05_2_week/Code02_LexicographicBiggerSumOfR.java create mode 100644 算法周更班/class_2023_05_2_week/Code03_SmallestSufficientTeam.java create mode 100644 算法周更班/class_2023_05_2_week/Code04_PartitionToKEqualSumSubsets.java create mode 100644 算法周更班/class_2023_05_2_week/Code05_MinReverseOperations.java diff --git a/算法周更班/class_2023_05_2_week/Code01_ImageArrayAverageGotoHalf.java b/算法周更班/class_2023_05_2_week/Code01_ImageArrayAverageGotoHalf.java new file mode 100644 index 0000000..adf4df2 --- /dev/null +++ b/算法周更班/class_2023_05_2_week/Code01_ImageArrayAverageGotoHalf.java @@ -0,0 +1,193 @@ +package class_2023_05_2_week; + +import java.util.Arrays; + +// 来自华为OD,学员问题 +// 一个图像有n个像素点,存储在一个长度为n的数组arr里, +// 每个像素点的取值范围[0,s]的整数 +// 请你给图像每个像素点值加上一个整数k(可以是负数) +// 像素值会自动截取到[0,s]范围, +// 当像素值<0,会更改为0,当新像素值>s,会更改为s +// 这样就可以得到新的arr,想让所有像素点的平均值最接近中位值s/2, 向下取整 +// 请输出这个整数k, 如有多个整数k都满足, 输出小的那个 +// 1 <= n <= 10^6 +// 1 <= s <= 10^18 +public class Code01_ImageArrayAverageGotoHalf { + + // 暴力方法 + // 为了测试 + public static int best1(int[] arr, int s) { + int half = s / 2; + int average = -100000; + int ans = -s; + for (int k = -s; k <= s; k++) { + int curAverage = average1(arr, k, s); + if (Math.abs(half - curAverage) < Math.abs(half - average)) { + average = curAverage; + ans = k; + } + } + return ans; + } + + // 暴力方法 + // 为了测试 + // arr[100, k = 200 , s + // s + public static int average1(int[] arr, int k, int s) { + int sum = 0; + for (int num : arr) { + int value = num + k; + if (value < 0) { + sum += 0; + } else if (value > s) { + sum += s; + } else { + sum += value; + } + } + return sum / arr.length; + } + + // 优化了一下,但不是最优解 + public static int best2(int[] arr, int s) { + int l = -s; + int r = s; + int m = 0; + int half = s / 2; + int average = -s; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + int curAverage = average1(arr, m, s); + if ((Math.abs(half - curAverage) < Math.abs(half - average)) + || ((Math.abs(half - curAverage) == Math.abs(half - average)) && m < ans)) { + average = curAverage; + ans = m; + } + if (curAverage >= half) { + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + // 正式方法 + // 最优解 + // O(N * logN) + O(logS * logN) + public static int best3(int[] arr, int s) { + Arrays.sort(arr); + int[] sum = new int[arr.length]; + sum[0] = arr[0]; + for (int i = 1; i < arr.length; i++) { + sum[i] = sum[i - 1] + arr[i]; + } + int l = -s; + int r = s; + int m = 0; + int half = s / 2; + int average = -s; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + int curAverage = average3(arr, sum, m, s); + if ((Math.abs(half - curAverage) < Math.abs(half - average)) + || ((Math.abs(half - curAverage) == Math.abs(half - average)) && m < ans)) { + average = curAverage; + ans = m; + } + if (curAverage >= half) { + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int average3(int[] arr, int[] pre, int k, int s) { + int n = arr.length; + if (k < 0) { + int l = bsZero(arr, k); + int sum = rangeSum(pre, l + 1, n - 1); + return (sum + (k * (n - l - 1))) / n; + } else { + int r = bsS(arr, k, s); + int sum = rangeSum(pre, 0, r - 1); + return (sum + (k * r) + (s * (n - r))) / n; + } + } + + public static int bsZero(int[] arr, int k) { + int ans = -1; + int l = 0; + int r = arr.length - 1; + int m; + while (l <= r) { + m = (l + r) / 2; + if (arr[m] + k <= 0) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static int bsS(int[] arr, int k, int s) { + int ans = arr.length; + int l = 0; + int r = arr.length - 1; + int m; + while (l <= r) { + m = (l + r) / 2; + if (arr[m] + k >= s) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int rangeSum(int[] pre, int l, int r) { + if (l > r) { + return 0; + } + return l == 0 ? pre[r] : (pre[r] - pre[l - 1]); + } + + // 为了测试 + public static int[] randomArray(int n, int s) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * (s + 1)); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int N = 50; + int S = 500; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int s = (int) (Math.random() * S) + 1; + int[] arr = randomArray(n, s); + int ans1 = best1(arr, s); + int ans2 = best2(arr, s); + int ans3 = best3(arr, s); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2023_05_2_week/Code02_LexicographicBiggerSumOfR.java b/算法周更班/class_2023_05_2_week/Code02_LexicographicBiggerSumOfR.java new file mode 100644 index 0000000..0c61ba8 --- /dev/null +++ b/算法周更班/class_2023_05_2_week/Code02_LexicographicBiggerSumOfR.java @@ -0,0 +1,180 @@ +package class_2023_05_2_week; + +// 来自学员问题,来自真实笔试 +// 塔子哥最近在处理一些字符串相关的任务 +// 他喜欢 R 字符,因为在某些任务中,这个字符通常表示“正确”的结果 +// 另一方面,他不喜欢 B 字符,因为在某些任务中,这个字符通常表示“错误”的结果 +// 为了解决他的任务,塔子哥定义了字符串的权值为字符串中 R 字符的出现次数 +// 例如,对于字符串 BBRBRB,它的权值为 2,因为其中有 2 个 R 字符 +// 现在,塔子哥面临一个问题,他有一个长度为 n 的字符串 s,它仅由 R 和 B 组成 +// 他想知道,长度为 n 的仅由 R 和 B组成的字符串中, +// 字典序不小于 s 的字符串的权值之和是多少? +// 因此,他需要编写一个程序来解决这个问题 +// 输入第一行为一个整数 n ,表示字符串的长度 +// 输入第二行为一个长度为 n 的字符串 s ,字符串中元素组成仅为 R 和 B +// 输出一个整数,代表长度为 n 的、字典序不小于 s 的字符串权值之和 +// 输入样例: +// 3 +// RBR +// 输出: +// 7 +// 解释:共有 3 个字符串字典序大于等于"RBR",RBR权值为2,RRB为2,RRR为3 +// 1 <= n <= 100000 +// 结果可能很大,对1000000007取模 +// 帖子链接 : https://www.mashibing.com/question/detail/67223 +public class Code02_LexicographicBiggerSumOfR { + + // 为了测试 + // 暴力方法 + public static int sum1(String str) { + return process1("", str); + } + + // 为了测试 + // 暴力方法 + public static int process1(String path, String s) { + if (path.length() == s.length()) { + if (path.compareTo(s) >= 0) { + int ans = 0; + for (int i = 0; i < path.length(); i++) { + if (path.charAt(i) == 'R') { + ans++; + } + } + return ans; + } else { + return 0; + } + } else { + return process1(path + "R", s) + process1(path + "B", s); + } + } + + // 以下为正式方法 + public static int MAXN = 100001; + + // pow2[i] : 长度为i的所有RB串,一共有多少字符串,%mod的余数 + // 一定要求余数! + public static int[] pow2 = new int[MAXN]; + + // f[i] : 长度为i的所有RB串,一共有多少权值和,%mod的余数 + // 一定要求余数! + public static int[] f = new int[MAXN]; + + public static int mod = 1000000007; + + static { + pow2[0] = 1; + for (int i = 1; i < MAXN; i++) { + pow2[i] = (pow2[i - 1] * 2) % mod; + } + f[1] = 1; + for (int i = 2; i < MAXN; i++) { + // 2^i-1 + 2*f[i-1] + f[i] = (pow2[i - 1] + f[i - 1]) % mod; + f[i] = (f[i] + f[i - 1]) % mod; + } + } + + // 普通递归版 + // 不是最优解,但是展示了大过程 + public static int sum2(String str) { + int n = str.length(); + char[] s = str.toCharArray(); + int[] rnumber = new int[n]; + rnumber[0] = s[0] == 'R' ? 1 : 0; + for (int i = 1; i < n; i++) { + rnumber[i] = rnumber[i - 1] + (s[i] == 'R' ? 1 : 0); + } + return process2(s, rnumber, n, 0); + } + + // 你依次填写字符串 + // 0...i-1范围上,你填写的东西,和s完全一样 + // 当前来到i位置,一共的长度是n + // rnumber[i] : s[0...i]范围上有几个R + // 返回 : 在[0...i-1]和s完全一样的情况下,后续所有字典序不小于s的字符串,整体的权值和是多少? + public static int process2(char[] s, int[] rnumber, int n, int i) { + int ans = 0; + if (i == n) { + ans = rnumber[n - 1]; + } else { + if (s[i] == 'B') { + // s当前位置是'B',你可以填写R,也可以是B + // 如果你当前填R,现在的情况是: + // 0...i-1位置上你填写的和s一样; + // i位置上s是'B',你填写的是'R'; + // i之后的剩余长度是 : n-i-1,你可以随便填了 + // n-i-1可以随便填,那么字符串个数为 : 2^(n-i-1)个 + // 这2^(n-i-1)个字符串,都是: + // [0...i-1]和s一样,i位置比s多一个R, + // 权值和 = 1) + 2) + // 1) 是这2^(n-i-1)个字符串,前缀上的权值和 + // 2) 是这2^(n-i-1)个字符串,后缀上的权值和 + // 具体来说 : + // 1) (s的[0...i]范围上R的数量 + 1) * 2^(n-i-1) + // 2) 在[i+1....]范围上的权值和 : f[n-i-1] + int p1 = (int) (((long) (rnumber[i] + 1) * pow2[n - i - 1]) % mod); + p1 = (p1 + f[n - i - 1]) % mod; + // 如果你当前填B,那么继续递归 + int p2 = process2(s, rnumber, n, i + 1); + ans = (p1 + p2) % mod; + } else { + // s当前位置是'R',你只能填写R,然后继续递归 + ans = process2(s, rnumber, n, i + 1); + } + } + return ans; + } + + // 彻底动态规划版 + // 正式版,时间复杂度O(N) + public static int sum3(String str) { + int n = str.length(); + char[] s = str.toCharArray(); + int[] rnumber = new int[n]; + rnumber[0] = s[0] == 'R' ? 1 : 0; + for (int i = 1; i < n; i++) { + rnumber[i] = rnumber[i - 1] + (s[i] == 'R' ? 1 : 0); + } + int[] dp = new int[n + 1]; + dp[n] = rnumber[n - 1]; + for (int i = n - 1; i >= 0; i--) { + if (s[i] == 'B') { + int p1 = (int) (((long) (rnumber[i] + 1) * pow2[n - i - 1]) % mod); + p1 = (p1 + f[n - i - 1]) % mod; + int p2 = dp[i + 1]; + dp[i] = (p1 + p2) % mod; + } else { + dp[i] = dp[i + 1]; + } + } + return dp[0]; + } + + // 为了测试 + public static String randomString(int n) { + char[] s = new char[n]; + for (int i = 0; i < n; i++) { + s[i] = Math.random() < 0.5 ? 'B' : 'R'; + } + return String.valueOf(s); + } + + public static void main(String[] args) { + int N = 15; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + String s = randomString(n); + int ans1 = sum1(s); + int ans3 = sum3(s); + if (ans1 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2023_05_2_week/Code03_SmallestSufficientTeam.java b/算法周更班/class_2023_05_2_week/Code03_SmallestSufficientTeam.java new file mode 100644 index 0000000..0773335 --- /dev/null +++ b/算法周更班/class_2023_05_2_week/Code03_SmallestSufficientTeam.java @@ -0,0 +1,140 @@ +package class_2023_05_2_week; + +import java.util.Arrays; +import java.util.List; + +// 作为项目经理,你规划了一份需求的技能清单 req_skills, +// 并打算从备选人员名单 people 中选出些人组成一个「必要团队」 +//( 编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。 +// 所谓「必要团队」,就是在这个团队中, +// 对于所需求的技能列表 req_skills 中列出的每项技能, +// 团队中至少有一名成员已经掌握。可以用每个人的编号来表示团队中的成员: +// 例如,团队 team = [0, 1, 3] 表示掌握技能分别为 +// people[0],people[1],和 people[3] 的备选人员。 +// 请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。 +// 你可以按 任意顺序 返回答案,题目数据保证答案存在。 +// 测试链接 : https://leetcode.cn/problems/smallest-sufficient-team/ +public class Code03_SmallestSufficientTeam { + + // 需要的技能有 : "ABC" 、 "FG" 、 "ET" + // 人 : "ABC" "ET" "BB" + // "ABC" 、 "FG" 、 "ET" -> "ABC" "ET" "FG" + // 0 1 2 + // x : "ABC" "ET" "BB" : 2 1 0 + // 0 1 1 + public static int[] smallestSufficientTeam(String[] skills, List> people) { + Arrays.sort(skills); + int n = skills.length; + int m = people.size(); + int[] statuses = new int[m]; + for (int i = 0; i < m; i++) { + int skillStatus = 0; // 00000000000000 + List skill = people.get(i); + skill.sort((a, b) -> a.compareTo(b)); + int p1 = 0; + int p2 = 0; + while (p1 < n && p2 < skill.size()) { + int compare = skills[p1].compareTo(skill.get(p2)); + if (compare < 0) { + p1++; + } else if (compare > 0) { + p2++; + } else { + skillStatus |= 1 << p1; + p1++; + p2++; + } + } + statuses[i] = skillStatus; + } + // 上面的过程,其实是把技能变成状态信息 + // 比如: + // skills = { f,a,e,c} + // 排个序为 : a c e f + // 认为a是0技能 + // 认为c是1技能 + // 认为e是2技能 + // 认为f是3技能 + // 然后就可以把每个人的技能变成状态信息了 + // 比如,A会的技能 : e f t x c + // feca + // 认为A会的技能状态为 : 1110 + // 解释一下 : + // 第0位是0,说明A不会a技能 + // 第1位是1,说明A会c技能 + // 第2位是1,说明A会e技能 + // 第3位是1,说明A会f技能 + // 至于t、x技能,忽略!因为没用 + // 上面的过程,就是把技能编号 + // 然后把每个人的技能变成位信息 + // 比如skills一共8个技能 + // 那么就是为了这个状态 : 00..0011111111 + // 也就是什么时候凑齐8个1,什么时候就可以结束了! + int[][] dp = new int[m][1 << n]; + for (int i = 0; i < m; i++) { + Arrays.fill(dp[i], -1); + } + // process函数去跑动态规划 + // 目的是算出至少几人 + // 以及填好动态规划表 + int size = process(statuses, n, 0, 0, dp); + int[] ans = new int[size]; + int ansi = 0; + int i = 0; + int status = 0; + // 大厂刷题班,章节11 + // 看一下,是根据动态规划表生成答案的路径 + // 这就是为什么需要动态规划表,因为答案的路径可以用动态规划表生成 + // 一定要看一下这个课 + while (status != (1 << n) - 1) { + // 3人 3人 + if (i + 1 == m || dp[i][status] != dp[i + 1][status]) { + ans[ansi++] = i; + status |= statuses[i]; + } + i++; + } + return ans; + } + + // 第i个人的技能列表,是位信息!就是people[i]!people数组是固定参数 + // 一定要凑齐n个技能,n是固定参数 + // 当前来到第i个人了,可以要这个人,也可以不要这个人,i是可变信息! + // 之前的技能,哪个技能凑到了,哪个技能还没凑到,就是status!你的目的是把它凑齐!也就是凑齐n个1!stauts是可变参数 + // 返回值的含义 : + // 当前在people[i....]范围上选择人,之前凑齐的技能状态是status,请问所有技能都凑齐,还需要至少几个人 + // dp就是动态规划表,记录返回值的! + public static int process(int[] people, int n, int i, int status, int[][] dp) { + // n = 8 + // 0000...0000 1111 1111 + if (status == (1 << n) - 1) { + // 技能已经凑齐了 + // 还需要0个人 + return 0; + } + // 还没凑齐! + if (i == people.length) { + // 技能还没凑齐 + // 但是人已经没了 + // 怎么都凑不齐了 + return Integer.MAX_VALUE; + } + if (dp[i][status] != -1) { + // 缓存命中,直接返回 + return dp[i][status]; + } + // 不要第i个人,后续要几个人能凑齐 + int p1 = process(people, n, i + 1, status, dp); + // 要第i个人,后续要几个人能凑齐 + int p2 = Integer.MAX_VALUE; + int next2 = process(people, n, i + 1, status | people[i], dp); + if (next2 != Integer.MAX_VALUE) { + p2 = 1 + next2; + } + // 选择人数最少的方案 + int ans = Math.min(p1, p2); + dp[i][status] = ans; + return ans; + } + +} diff --git a/算法周更班/class_2023_05_2_week/Code04_PartitionToKEqualSumSubsets.java b/算法周更班/class_2023_05_2_week/Code04_PartitionToKEqualSumSubsets.java new file mode 100644 index 0000000..ab016e0 --- /dev/null +++ b/算法周更班/class_2023_05_2_week/Code04_PartitionToKEqualSumSubsets.java @@ -0,0 +1,94 @@ +package class_2023_05_2_week; + +import java.util.Arrays; + +// 给定一个整数数组 nums 和一个正整数 k, +// 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 +// 测试链接 : https://leetcode.cn/problems/partition-to-k-equal-sum-subsets/ +public class Code04_PartitionToKEqualSumSubsets { + + // 状态压缩的方法,正统,处理不了大长度 + // 其实任何方法都处理不了 + public static boolean canPartitionKSubsets1(int[] nums, int k) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % k != 0) { + return false; + } + return process1(nums, 0, 0, 0, sum / k, k, new int[1 << nums.length]) == 1; + } + + public static int process1(int[] nums, int status, int sum, int sets, int limit, int k, int[] dp) { + if (dp[status] != 0) { + return dp[status]; + } + int ans = -1; + if (sets == k) { + ans = 1; + } else { + for (int i = 0; i < nums.length; i++) { + if ((status & (1 << i)) == 0 && sum + nums[i] <= limit) { + if (sum + nums[i] == limit) { + ans = process1(nums, status | (1 << i), 0, sets + 1, limit, k, dp); + } else { + ans = process1(nums, status | (1 << i), sum + nums[i], sets, limit, k, dp); + } + if (ans == 1) { + break; + } + } + } + } + dp[status] = ans; + return ans; + } + + public static boolean canPartitionKSubsets2(int[] nums, int k) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % k != 0) { + return false; + } + Arrays.sort(nums); + return partitionK(new int[k], sum / k, nums, nums.length - 1); + } + + // 100 10 -> 10 + // 100 2 -> 50 + // 100 4 -> 25 + // 当前的数字,nums[index] + // nums[n-1] 给哪个桶 + // nums[n-2] 给哪个桶 + // ... + // nums[0] 给哪个桶 + public static boolean partitionK(int[] group, int target, int[] nums, int index) { + if (index < 0) { + return true; + } + int num = nums[index]; + // len是桶的数量! 就是k + int len = group.length; + for (int i = 0; i < len; i++) { + if (group[i] + num <= target) { + // 放入后,不超 + group[i] += num; + if (partitionK(group, target, nums, index - 1)) { + return true; + } + group[i] -= num; +// if (group[i] == 0) { +// return false; +// } + while (i + 1 < group.length && group[i] == group[i + 1]) { + i++; + } + } + } + return false; + } + +} diff --git a/算法周更班/class_2023_05_2_week/Code05_MinReverseOperations.java b/算法周更班/class_2023_05_2_week/Code05_MinReverseOperations.java new file mode 100644 index 0000000..1c7fce2 --- /dev/null +++ b/算法周更班/class_2023_05_2_week/Code05_MinReverseOperations.java @@ -0,0 +1,68 @@ +package class_2023_05_2_week; + +import java.util.Arrays; +import java.util.TreeSet; + +// 给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p , +// 它们表示一个长度为 n 且下标从 0 开始的数组 arr , +// 数组中除了下标为 p 处是 1 以外,其他所有数都是 0 。 +// 同时给你一个整数数组 banned ,它包含数组中的一些位置。 +// banned 中第 i 个位置表示 arr[banned[i]] = 0 ,题目保证 banned[i] != p 。 +// 你可以对 arr 进行 若干次 操作。一次操作中,你选择大小为 k 的一个 子数组 +// 并将它 翻转 。在任何一次翻转操作后, +// 你都需要确保 arr 中唯一的 1 不会到达任何 banned 中的位置。 +// 换句话说,arr[banned[i]] 始终 保持 0 。 +// 请你返回一个数组 ans ,对于 [0, n - 1] 之间的任意下标 i , +// ans[i] 是将 1 放到位置 i 处的 最少 翻转操作次数, +// 如果无法放到位置 i 处,此数为 -1 。 +// 子数组 指的是一个数组里一段连续 非空 的元素序列。 +// 对于所有的 i ,ans[i] 相互之间独立计算。 +// 将一个数组中的元素 翻转 指的是将数组中的值变成 相反顺序 。 +// 测试链接 : https://leetcode.cn/problems/minimum-reverse-operations/ +public class Code05_MinReverseOperations { + + public static int[] minReverseOperations(int n, int p, int[] banned, int k) { + TreeSet oddSet = new TreeSet<>(); + TreeSet evenSet = new TreeSet<>(); + for (int i = 1; i < n; i += 2) { + oddSet.add(i); + } + for (int i = 0; i < n; i += 2) { + evenSet.add(i); + } + for (int ban : banned) { + oddSet.remove(ban); + evenSet.remove(ban); + } + oddSet.remove(p); + evenSet.remove(p); + int[] ans = new int[n]; + Arrays.fill(ans, -1); + int[] queue = new int[n]; + int l = 0; + int r = 0; + queue[r++] = p; + int level = 0; + while (l < r) { + int end = r; + for (; l < end; l++) { + int cur = queue[l]; + ans[cur] = level; + // 核心的两句,举例子说明 + // 纯下标变换,没什么算法 + int left = Math.max(cur - k + 1, k - cur - 1); + int right = Math.min(cur + k - 1, n * 2 - k - cur - 1); + TreeSet curSet = (left & 1) == 1 ? oddSet : evenSet; + Integer ceilling = curSet.ceiling(left); + while (ceilling != null && ceilling <= right) { + queue[r++] = ceilling; + curSet.remove(ceilling); + ceilling = curSet.ceiling(left); + } + } + level++; + } + return ans; + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 68d3575..1c7007d 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -3549,6 +3549,75 @@ B = { b1, b2, ... ,bn }。 +第068节 2023年5月第2周流行算法题目解析 + +来自华为OD,学员问题 +一个图像有n个像素点,存储在一个长度为n的数组arr里, +每个像素点的取值范围[0,s]的整数 +请你给图像每个像素点值加上一个整数k(可以是负数) +像素值会自动截取到[0,s]范围, +当像素值<0,会更改为0,当新像素值>s,会更改为s +这样就可以得到新的arr,想让所有像素点的平均值最接近中位值s/2, 向下取整 +请输出这个整数k, 如有多个整数k都满足, 输出小的那个 +1 <= n <= 10^6 +1 <= s <= 10^18 + +来自学员问题,来自真实笔试 +塔子哥最近在处理一些字符串相关的任务 +他喜欢 R 字符,因为在某些任务中,这个字符通常表示“正确”的结果 +另一方面,他不喜欢 B 字符,因为在某些任务中,这个字符通常表示“错误”的结果 +为了解决他的任务,塔子哥定义了字符串的权值为字符串中 R 字符的出现次数 +例如,对于字符串 BBRBRB,它的权值为 2,因为其中有 2 个 R 字符 +现在,塔子哥面临一个问题,他有一个长度为 n 的字符串 s,它仅由 R 和 B 组成 +他想知道,长度为 n 的仅由 R 和 B组成的字符串中, +字典序不小于 s 的字符串的权值之和是多少? +因此,他需要编写一个程序来解决这个问题 +输入第一行为一个整数 n ,表示字符串的长度 +输入第二行为一个长度为 n 的字符串 s ,字符串中元素组成仅为 R 和 B +输出一个整数,代表长度为 n 的、字典序不小于 s 的字符串权值之和 +输入样例: +3 +RBR +输出: +7 +解释:共有 3 个字符串字典序大于等于"RBR",RBR权值为2,RRB为2,RRR为3 +1 <= n <= 100000 +结果可能很大,对1000000007取模 +帖子链接 : https://www.mashibing.com/question/detail/67223 + +作为项目经理,你规划了一份需求的技能清单 req_skills, +并打算从备选人员名单 people 中选出些人组成一个「必要团队」 +编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表 +所谓「必要团队」,就是在这个团队中, +对于所需求的技能列表 req_skills 中列出的每项技能, +团队中至少有一名成员已经掌握。可以用每个人的编号来表示团队中的成员: +例如,团队 team = [0, 1, 3] 表示掌握技能分别为 +people[0],people[1],和 people[3] 的备选人员。 +请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。 +你可以按 任意顺序 返回答案,题目数据保证答案存在。 +测试链接 : https://leetcode.cn/problems/smallest-sufficient-team/ + +给定一个整数数组 nums 和一个正整数 k, +找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 +测试链接 : https://leetcode.cn/problems/partition-to-k-equal-sum-subsets/ + +给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p , +它们表示一个长度为 n 且下标从 0 开始的数组 arr , +数组中除了下标为 p 处是 1 以外,其他所有数都是 0 。 +同时给你一个整数数组 banned ,它包含数组中的一些位置。 +banned 中第 i 个位置表示 arr[banned[i]] = 0 ,题目保证 banned[i] != p 。 +你可以对 arr 进行 若干次 操作。一次操作中,你选择大小为 k 的一个 子数组 +并将它 翻转 。在任何一次翻转操作后, +你都需要确保 arr 中唯一的 1 不会到达任何 banned 中的位置。 +换句话说,arr[banned[i]] 始终 保持 0 。 +请你返回一个数组 ans ,对于 [0, n - 1] 之间的任意下标 i , +ans[i] 是将 1 放到位置 i 处的 最少 翻转操作次数, +如果无法放到位置 i 处,此数为 -1 。 +子数组 指的是一个数组里一段连续 非空 的元素序列。 +对于所有的 i ,ans[i] 相互之间独立计算。 +将一个数组中的元素 翻转 指的是将数组中的值变成 相反顺序 。 +测试链接 : https://leetcode.cn/problems/minimum-reverse-operations/ +