From 40d65b2dc63b023c7eaefe23d09cb238ad892dc2 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Thu, 3 Nov 2022 13:21:09 +0800 Subject: [PATCH] modify code --- .../Code01_DessertPriceClosedTarget.java | 191 ++++++++++++++++++ .../Code02_ScoreAllMatrix.java | 78 +++++++ .../Code03_HappyLimitLessGap.java | 125 ++++++++++++ .../Code04_ValueNoMoreXDigitsToY.java | 120 +++++++++++ .../Code05_CreateMinNumberFromPattern.java | 70 +++++++ ...养的大厂算法面试题(正在直播) | 69 ++++++- 6 files changed, 644 insertions(+), 9 deletions(-) create mode 100644 算法周更班/class_2022_11_1_week/Code01_DessertPriceClosedTarget.java create mode 100644 算法周更班/class_2022_11_1_week/Code02_ScoreAllMatrix.java create mode 100644 算法周更班/class_2022_11_1_week/Code03_HappyLimitLessGap.java create mode 100644 算法周更班/class_2022_11_1_week/Code04_ValueNoMoreXDigitsToY.java create mode 100644 算法周更班/class_2022_11_1_week/Code05_CreateMinNumberFromPattern.java diff --git a/算法周更班/class_2022_11_1_week/Code01_DessertPriceClosedTarget.java b/算法周更班/class_2022_11_1_week/Code01_DessertPriceClosedTarget.java new file mode 100644 index 0000000..94add91 --- /dev/null +++ b/算法周更班/class_2022_11_1_week/Code01_DessertPriceClosedTarget.java @@ -0,0 +1,191 @@ +package class_2022_11_1_week; + +import java.util.Arrays; +import java.util.TreeSet; + +// 来自华为 +// 做甜点需要购买配料,目前共有n种基料和m种配料可供选购 +// 制作甜点需要遵循以下几条规则: +// 必须选择1种基料;可以添加0种、1种或多种配料,每种类型的配料最多添加2份 +// 给定长度为n的数组base, base[i]表示第i种基料的价格 +// 给定长度为m的数组topping, topping[j]表示第j种配料的价格 +// 给定一个正数target,表示你做的甜点最终的价格要尽量接近这个数值 +// 返回最接近这个数值的价格是多少 +// 如果有多个方案,都最接近target,返回价格最小的那个答案 +// 1 <= n,m <= 10 +// 1 <= base[i], topping[j] <= 10 ^ 4 +// 1 <= target <= 10 ^ 4 +public class Code01_DessertPriceClosedTarget { + + // 方法1,用有序表的方法 + public static int closedTarget1(int[] base, int[] topping, int target) { + // 辅料所能产生的所有价格! + // 0 5 15 23 + TreeSet set = new TreeSet<>(); + // 暴力展开!收集所有能产生的价格!放入辅料表里去! + process1(topping, 0, 0, set); + int ans = Integer.MAX_VALUE; + for (int num : base) { + // 枚举每一种主料的价格! + // 最终能搭配出来的最接近的价格 + int cur = num; + // 20 100 + // 110 100 + if (num < target) { // cur < 要求 + // 60 100 + // 40 + int rest = target - num; + // <= rest 最接近的! + Integer floor = set.floor(rest); + // >= rest 最接近的! + Integer ceiling = set.ceiling(rest); + if (floor == null || ceiling == null) { + cur += floor == null ? ceiling : floor; + } else { + cur += rest - floor <= ceiling - rest ? floor : ceiling; + } + // cur会选择floor,或ceiling,谁加上最接近target选谁! + } + if (Math.abs(cur - target) < Math.abs(ans - target) + || (Math.abs(cur - target) == Math.abs(ans - target) && cur < ans)) { + ans = cur; + } + } + return ans; + } + + // 暴力展开!收集所有能产生的价格!放入辅料表里去! + // topping[index....] + // topping[0...index-1] sum + public static void process1(int[] topping, int index, int sum, TreeSet set) { + if (index == topping.length) { + set.add(sum); + } else { + process1(topping, index + 1, sum, set); + process1(topping, index + 1, sum + topping[index], set); + process1(topping, index + 1, sum + (topping[index] << 1), set); + } + } + + // 方法2,用数组排序+二分的方法 + + public static int[] collect = new int[14348907]; + + public static int size = 0; + + public static int closedTarget2(int[] base, int[] topping, int target) { + size = 0; + process2(topping, 0, 0); + Arrays.sort(collect, 0, size); + int ans = Integer.MAX_VALUE; + for (int num : base) { + int cur = num; + if (num < target) { + int rest = target - num; + int floor = floor(rest); + int ceiling = ceiling(rest); + if (floor == -1 || ceiling == -1) { + cur += floor == -1 ? ceiling : floor; + } else { + cur += rest - floor <= ceiling - rest ? floor : ceiling; + } + } + if (Math.abs(cur - target) < Math.abs(ans - target) + || (Math.abs(cur - target) == Math.abs(ans - target) && cur < ans)) { + ans = cur; + } + } + return ans; + } + + public static void process2(int[] topping, int index, int sum) { + if (index == topping.length) { + collect[size++] = sum; + } else { + process2(topping, index + 1, sum); + process2(topping, index + 1, sum + topping[index]); + process2(topping, index + 1, sum + (topping[index] << 1)); + } + } + + public static int floor(int num) { + int l = 0; + int r = size - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (collect[m] <= num) { + ans = collect[m]; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static int ceiling(int num) { + int l = 0; + int r = size - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (collect[m] >= num) { + ans = collect[m]; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + // 为了验证 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了验证 + public static void main(String[] args) { + int N = 8; + int V = 10000; + int testTime = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int m = (int) (Math.random() * N) + 1; + int[] base = randomArray(n, V); + int[] topping = randomArray(m, V); + int target = (int) (Math.random() * V) + 1; + int ans1 = closedTarget1(base, topping, target); + int ans2 = closedTarget2(base, topping, target); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + int n = 15; + int m = 15; + int[] base = randomArray(n, V); + int[] topping = randomArray(m, V); + int target = (int) (Math.random() * V) + 1; + System.out.println("base数组长度 : " + n); + System.out.println("topping数组长度 : " + m); + System.out.println("数值范围 : " + V); + long start = System.currentTimeMillis(); + closedTarget2(base, topping, target); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + + } + +} diff --git a/算法周更班/class_2022_11_1_week/Code02_ScoreAllMatrix.java b/算法周更班/class_2022_11_1_week/Code02_ScoreAllMatrix.java new file mode 100644 index 0000000..c5a570b --- /dev/null +++ b/算法周更班/class_2022_11_1_week/Code02_ScoreAllMatrix.java @@ -0,0 +1,78 @@ +package class_2022_11_1_week; + +// 来自蚂蚁金服 +// 得分的定义 : +// 含有大小2*2的矩阵,要么: +// 1 0 +// 0 1 可以得1分 +// 要么 +// 0 1 +// 1 0 可以得1分 +// 那么一个任意大小的矩阵就有若干得分点,比如 +// 0 1 0 +// 1 0 1 +// 这个矩阵就有2个得分点 +// 给定正数N,正数M,求所有可能的情况里,所有的得分点总和 +// 1 <= N、M <= 10^9 +public class Code02_ScoreAllMatrix { + + public static int score1(int n, int m) { + if (n < 2 || m < 2) { + return 0; + } + int[][] matrix = new int[n][m]; + return process(matrix, 0, 0, n, m); + } + + public static int process(int[][] matrix, int i, int j, int n, int m) { + if (i == n) { + int score = 0; + for (int r = 1; r < n; r++) { + for (int c = 1; c < m; c++) { + if (check(matrix, r, c)) { + score++; + } + } + } + return score; + } + if (j == m) { + return process(matrix, i + 1, 0, n, m); + } + int score = 0; + matrix[i][j] = 1; + score += process(matrix, i, j + 1, n, m); + matrix[i][j] = 0; + score += process(matrix, i, j + 1, n, m); + return score; + } + + public static boolean check(int[][] m, int r, int c) { + return (m[r - 1][c - 1] == 0 && m[r][c - 1] == 1 && m[r - 1][c] == 1 && m[r][c] == 0) + || (m[r - 1][c - 1] == 1 && m[r][c - 1] == 0 && m[r - 1][c] == 0 && m[r][c] == 1); + } + + public static int score2(int n, int m) { + if (n < 2 || m < 2) { + return 0; + } + // n <= 10^9 + // m <= 10^9 + // 取mod + // (n * m - m - n + 1) -> O(1) + // 2^(n * m - 3) ??? + // 真实的笔试场景下: + // 算2^(k)次方的 + // 体系学习班,章节27,学习快速幂 + // 本代码,不处理mod + return (n * m - m - n + 1) * (1 << (n * m - 3)); + } + + public static void main(String[] args) { + int n = 3; + int m = 4; + System.out.println(score1(n, m)); + System.out.println(score2(n, m)); + } + +} diff --git a/算法周更班/class_2022_11_1_week/Code03_HappyLimitLessGap.java b/算法周更班/class_2022_11_1_week/Code03_HappyLimitLessGap.java new file mode 100644 index 0000000..e282f20 --- /dev/null +++ b/算法周更班/class_2022_11_1_week/Code03_HappyLimitLessGap.java @@ -0,0 +1,125 @@ +package class_2022_11_1_week; + +import java.util.Arrays; + +// 来自蚂蚁金服 +// 小红有n个朋友, 她准备开个宴会,邀请一些朋友 +// i号朋友的愉悦值为a[i],财富值为b[i] +// 如果两个朋友同时参加宴会,这两个朋友之间的隔阂是其财富值差值的绝对值 +// 宴会的隔阂值,是财富差距最大的两人产生的财富值差值的绝对值 +// 宴会的愉悦值,是所有参加宴会朋友的愉悦值总和 +// 小红可以邀请任何人, +// 希望宴会的愉悦值不能小于k的情况下, 宴会的隔阂值能最小是多少 +// 如果做不到,返回-1 +// 1 <= n <= 2 * 10^5 +// 1 <= 愉悦值、财富值 <= 10^9 +// 1 <= k <= 10^14 +public class Code03_HappyLimitLessGap { + + // 暴力方法 + // 为了验证 + public static int lessGap1(int[] a, int[] b, int k) { + long ans = process(a, b, 0, k, Integer.MAX_VALUE, Integer.MIN_VALUE); + return ans < Integer.MAX_VALUE ? (int) ans : -1; + } + + // 暴力方法 + // 为了验证 + public static long process(int[] a, int[] b, int i, int rest, int min, int max) { + if (rest <= 0) { + return (long) max - (long) min; + } + if (i == a.length) { + return (long) Integer.MAX_VALUE; + } + long p1 = process(a, b, i + 1, rest, min, max); + long p2 = process(a, b, i + 1, rest - a[i], Math.min(min, b[i]), Math.max(max, b[i])); + return Math.min(p1, p2); + } + + // 正式方法 + // 二分答案 + public static int lessGap2(int[] a, int[] b, long k) { + int n = a.length; + // a : 20 30 17 + // b : 5 10 36 + // 0 1 2 + // [ 20, 5] [30, 10] [17, 36] + // 0 1 2 + int[][] f = new int[n][2]; + int min = b[0]; + int max = b[0]; + for (int i = 0; i < n; i++) { + f[i][0] = a[i]; + f[i][1] = b[i]; + min = Math.min(min, b[i]); + max = Math.max(max, b[i]); + } + // 排序和大流程,没关系 + // 是子函数,maxHappy函数,需要用到,排了序 + // 根据财富排序,少 -> 多 + Arrays.sort(f, (x, y) -> x[1] - y[1]); + // 隔阂值的范围 l ~ r + int l = 0; + int r = max - min; + int m = 0; + int ans = -1; + while (l <= r) { + // 0.........50 + // 25 + m = (l + r) / 2; + if (maxHappy(f, m) >= k) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static long maxHappy(int[][] f, int limit) { + int n = f.length; + long sum = 0; + long ans = 0; + for (int l = 0, r = 0; l < n; l++) { + while (r < n && f[r][1] - f[l][1] <= limit) { + sum += f[r++][0]; + } + ans = Math.max(ans, sum); + sum -= f[l][0]; + r = Math.max(r, l + 1); + } + return ans; + } + + // 为了验证 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了验证 + public static void main(String[] args) { + int N = 15; + int V = 20; + int testTime = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[] a = randomArray(n, V); + int[] b = randomArray(n, V); + int k = (int) (Math.random() * n * V) + 1; + int ans1 = lessGap1(a, b, k); + int ans2 = lessGap2(a, b, k); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + } + +} diff --git a/算法周更班/class_2022_11_1_week/Code04_ValueNoMoreXDigitsToY.java b/算法周更班/class_2022_11_1_week/Code04_ValueNoMoreXDigitsToY.java new file mode 100644 index 0000000..cd8a08b --- /dev/null +++ b/算法周更班/class_2022_11_1_week/Code04_ValueNoMoreXDigitsToY.java @@ -0,0 +1,120 @@ +package class_2022_11_1_week; + +import java.util.Arrays; + +// 来自CISCO +// 给定两个正整数x、y,都是int整型(java里) +// 返回0 ~ x以内,每位数字加起来是y的数字个数 +// 比如,x = 20、y = 5,返回2 +// 因为0 ~ x以内,每位数字加起来是5的数字有:5、14 +// x、y范围是java里正整数的范围 +// x <= 2 * 10^9 +// y <= 90 +public class Code04_ValueNoMoreXDigitsToY { + + // 暴力方法 + // 为了测试 + public static int num1(int x, int y) { + int ans = 0; + for (int i = 0; i <= x; i++) { + if (check1(i, y)) { + ans++; + } + } + return ans; + } + + public static boolean check1(int num, int y) { + int sum = 0; + while (num != 0) { + sum += num % 10; + num /= 10; + } + return sum == y; + } + + // 正式方法 + // 时间复杂度O(整数有几个十进制位的平方 * 9) + public static int num2(int x, int y) { + if (x < 0 || y > 90) { + return 0; + } + if (x == 0) { + return y == 0 ? 1 : 0; + } + // x : 352764 + // len : 6 + // offset : 100000 + int offset = 1; + int len = 1; + while (offset <= x / 10) { + offset *= 10; + len++; + } + int[][] dp = new int[len + 1][y + 1]; + for (int i = 0; i <= len; i++) { + Arrays.fill(dp[i], -1); + } + return count(x, offset, len, y, dp); + } + + // x, 0~x 范围,固定的! + // abcdef + // offset, 100000, 当前关心的是哪位数字 + // 10000 + // 1000 + // len, 还剩几位数字没处理完! + // rest, 每位数字的和加起来,要求是多少 + // 0~x ,在之前决定的数字不管,还剩下的数字能变出多少达标的! + public static int count(int x, int offset, int len, int rest, int[][] dp) { + if (len == 0) { + return rest == 0 ? 1 : 0; + } + if (dp[len][rest] != -1) { + return dp[len][rest]; + } + int ans = 0; + // x = 3457 + // 3 + // cur = 3 + // 0 ? len-1 rest - 0 + // 1 ? len-1 rest - 1 + // 2 ? len-1 rest - 2 + int cur = (x / offset) % 10; + for (int i = 0; i < cur && i <= rest; i++) { + ans += form[len - 1][rest - i]; + } + // cur = 3 2 + // 3 5 + if (cur <= rest) { // cur! + ans += count(x, offset / 10, len - 1, rest - cur, dp); + } + dp[len][rest] = ans; + return ans; + } + + // 打表了 + // from[i][j] : 一共有i位,所有的数字中,每一位加起来是j的数,有几个 + public static int[][] form = new int[11][91]; + static { + form[0][0] = 1; + for (int len = 1; len <= 10; len++) { + for (int sum = 0; sum <= len * 9; sum++) { + for (int cur = 0; cur <= 9 && cur <= sum; cur++) { + form[len][sum] += form[len - 1][sum - cur]; + } + } + } + } + + // 为了测试 + public static void main(String[] args) { + System.out.println(Integer.MAX_VALUE); + + int x = 88739128; + int y = 37; + System.out.println(num1(x, y)); + System.out.println(num2(x, y)); + } + +} diff --git a/算法周更班/class_2022_11_1_week/Code05_CreateMinNumberFromPattern.java b/算法周更班/class_2022_11_1_week/Code05_CreateMinNumberFromPattern.java new file mode 100644 index 0000000..ea31f95 --- /dev/null +++ b/算法周更班/class_2022_11_1_week/Code05_CreateMinNumberFromPattern.java @@ -0,0 +1,70 @@ +package class_2022_11_1_week; + +// 给你下标从 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。 +// 测试链接 : https://leetcode.cn/problems/construct-smallest-number-from-di-string/ +public class Code05_CreateMinNumberFromPattern { + + public static String smallestNumber(String pattern) { + return String.valueOf(create(pattern.toCharArray(), 0, 0, 0)); + } + + + // pattern I I I D + // 0 1 2 i + // 1 3 4 5 2 + // -1 + + // 1589 + // 9 8 5 4 3 2 1 0 + // 1 1 1 0 0 0 1 0 + // number = 1589 + // 返回 i... 所有数字都决定了,并且不破坏pattern,并且1~9每个数字最多用一次 + // 能出来的最小值是啥,返回 + public static int create(char[] pattern, int index, int status, int number) { + if (index == pattern.length + 1) { + return number; + } + int cur = 0; + while ((cur = next(status, cur)) != -1) { + // cur == 0 , 当前位,1 X + // cur == 1 , 当前位,2 X + // cur == 2, 当前位,4 + // partern I D > + // 0 1 2 3 + // ? ? ? ? + // D + // 0 1 + // 5 ? + if (index == 0 + || + (pattern[index - 1] == 'I' && number % 10 < cur) || (pattern[index - 1] == 'D' && number % 10 > cur)) { + int ans = create(pattern, index + 1, status | (1 << cur), number * 10 + cur); + if (ans != -1) { + return ans; + } + } + } + return -1; + } + + // status : + // 9 8 7 6 5 4 3 2 1 0 + // 1 1 1 1 1 1 0 0 1 0 + // 返回没有使用,且 > num, 最小的数字 + // num = 3 + public static int next(int status, int num) { + for (int i = num + 1; i <= 9; i++) { + if ((status & (1 << i)) == 0) { + return i; + } + } + return -1; + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 2c9646d..28af89e 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -2238,7 +2238,7 @@ n <= 3 * 10^5 -第046节 2022年10月第3周流行算法题目解析 +第046节 2022年10月第4周流行算法题目解析 来自华为 若两个正整数的和为素数,则这两个正整数称之为"素数伴侣" @@ -2326,15 +2326,66 @@ n <= 3 * 10^5 +第047节 2022年11月第1周流行算法题目解析 - - - - - - - - +来自华为 +做甜点需要购买配料,目前共有n种基料和m种配料可供选购 +制作甜点需要遵循以下几条规则: +必须选择1种基料;可以添加0种、1种或多种配料,每种类型的配料最多添加2份 +给定长度为n的数组base, base[i]表示第i种基料的价格 +给定长度为m的数组topping, topping[j]表示第j种配料的价格 +给定一个正数target,表示你做的甜点最终的价格要尽量接近这个数值 +返回最接近这个数值的价格是多少 +如果有多个方案,都最接近target,返回价格最小的那个答案 +1 <= n,m <= 10 +1 <= base[i], topping[j] <= 10 ^ 4 +1 <= target <= 10 ^ 4 + +来自蚂蚁金服 +得分的定义 : +含有大小2*2的矩阵,要么: +1 0 +0 1 可以得1分 +要么 +0 1 +1 0 可以得1分 +那么一个任意大小的矩阵就有若干得分点,比如 +0 1 0 +1 0 1 +这个矩阵就有2个得分点 +给定正数N,正数M,求所有可能的情况里,所有的得分点总和 +1 <= N、M <= 10^9 + +来自蚂蚁金服 +小红有n个朋友, 她准备开个宴会,邀请一些朋友 +i号朋友的愉悦值为a[i],财富值为b[i] +如果两个朋友同时参加宴会,这两个朋友之间的隔阂是其财富值差值的绝对值 +宴会的隔阂值,是财富差距最大的两人产生的财富值差值的绝对值 +宴会的愉悦值,是所有参加宴会朋友的愉悦值总和 +小红可以邀请任何人, +希望宴会的愉悦值不能小于k的情况下, 宴会的隔阂值能最小是多少 +如果做不到,返回-1 +1 <= n <= 2 * 10^5 +1 <= 愉悦值、财富值 <= 10^9 +1 <= k <= 10^14 + +来自CISCO +给定两个正整数x、y,都是int整型(java里) +返回0 ~ x以内,每位数字加起来是y的数字个数 +比如,x = 20、y = 5,返回2 +因为0 ~ x以内,每位数字加起来是5的数字有:5、14 +x、y范围是java里正整数的范围 +x <= 2 * 10^9 +y <= 90 + +给你下标从 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。 +测试链接 : https://leetcode.cn/problems/construct-smallest-number-from-di-string/