From 125fba984904bd3679cd9b1aab3ff57929972165 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Wed, 5 Jul 2023 22:39:23 +0800 Subject: [PATCH] modify code --- .../Code01_KillMonsterEverySkillUseOnce.java | 113 ++++++++++++++ ..._SubstringWithConcatenationOfAllWords.java | 133 ++++++++++++++++ .../Code03_StrongestForceField.java | 88 +++++++++++ ...Code04_SwapRowOrColMakeDiagonalAllOne.java | 142 ++++++++++++++++++ ...养的大厂算法面试题(正在直播) | 60 ++++++++ 5 files changed, 536 insertions(+) create mode 100644 算法周更班/class_2023_07_1_week/Code01_KillMonsterEverySkillUseOnce.java create mode 100644 算法周更班/class_2023_07_1_week/Code02_SubstringWithConcatenationOfAllWords.java create mode 100644 算法周更班/class_2023_07_1_week/Code03_StrongestForceField.java create mode 100644 算法周更班/class_2023_07_1_week/Code04_SwapRowOrColMakeDiagonalAllOne.java diff --git a/算法周更班/class_2023_07_1_week/Code01_KillMonsterEverySkillUseOnce.java b/算法周更班/class_2023_07_1_week/Code01_KillMonsterEverySkillUseOnce.java new file mode 100644 index 0000000..551ef3b --- /dev/null +++ b/算法周更班/class_2023_07_1_week/Code01_KillMonsterEverySkillUseOnce.java @@ -0,0 +1,113 @@ +package class_2023_07_1_week; + +// 先来一个最近国外同学考的题目 +// 已知一些供应点的位置,一共n个供应点 +// 其中有n-1个供应点一定都在x轴上,比如(15,0)位置,(2,0)位置等 +// 只有1个供应点不在x轴上,比如(23,17)位置 +// 给出每个供应点的位置,并且给定第k号供应点是出发点 +// 要求每个供应点最多走过2次,返回从k点出发,走完所有供应点的最少距离 +// 上面这个题没有代码实现 +// 因为这个题就是彻底的业务分析,只有一系列的贪心设计,代码也不难写 +// 同时这个题没有给出数据量,但是课上会讲O(n)的方法,所以也就无所谓了 +// 以下是这节课的正式题,来自学员问题 +// 现在有一个打怪类型的游戏,这个游戏是这样的,你有n个技能 +// 每一个技能会有一个伤害, +// 同时若怪物小于等于一定的血量,则该技能可能造成双倍伤害 +// 每一个技能最多只能释放一次,已知怪物有m点血量 +// 现在想问你最少用几个技能能消灭掉他(血量小于等于0) +// 技能的数量是n,怪物的血量是m +// i号技能的伤害是x[i],i号技能触发双倍伤害的血量最小值是y[i] +// 1 <= n <= 10 +// 1 <= m、x[i]、y[i] <= 10^6 +// 测试链接 : https://www.nowcoder.com/questionTerminal/d88ef50f8dab4850be8cd4b95514bbbd +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交以下的所有代码,并把主类名改成"Main" +// 可以直接通过 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_KillMonsterEverySkillUseOnce { + + public static int MAXN = 11; + + public static int[] kill = new int[MAXN]; + + public static int[] blood = new int[MAXN]; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + int t = (int) in.nval; + for (int i = 0; i < t; i++) { + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int j = 0; j < n; j++) { + in.nextToken(); + kill[j] = (int) in.nval; + in.nextToken(); + blood[j] = (int) in.nval; + } + int ans = f(n, 0, m); + out.println(ans == Integer.MAX_VALUE ? -1 : ans); + out.flush(); + } + } + } + + // n : 固定参数,一共有几个技能 + // kill: 技能伤害 + // blood : 血<=多少,触发双倍伤害 + // rest : 怪兽的剩余血量 + // i : 可变参数,全排列代码 + // kill[0...i-1] 使用过的技能,顺序就是从0到i-1 + // blood[0...i-1] 使用过的技能,顺序就是从0到i-1 + // i .... 最后,是还没有使用过的技能 + // 每一个没有使用过的技能,全试一遍 + public static int f(int n, int i, int rest) { + // 0 1 2 3 i(4) .. n-1 + if (rest <= 0) { + return i; + } + // 血 > 0 + if (i == n) { + return Integer.MAX_VALUE; + } + // 血 > 0 还有技能 + // ..... (y,i,s,k,e) + // ..... y.... + // ..... i.... + int ans = Integer.MAX_VALUE; + for (int j = i; j < n; j++) { + // j == i. i.....n-1 + // j 当前要释放的技能是谁! + swap(i, j); + if (rest > blood[i]) { + ans = Math.min(ans, f(n, i + 1, rest - kill[i])); + } else { + ans = Math.min(ans, f(n, i + 1, rest - kill[i] * 2)); + } + swap(i, j); + } + return ans; + } + + public static void swap(int i, int j) { + int a = kill[i]; + int b = blood[i]; + kill[i] = kill[j]; + blood[i] = blood[j]; + kill[j] = a; + blood[j] = b; + } + +} \ No newline at end of file diff --git a/算法周更班/class_2023_07_1_week/Code02_SubstringWithConcatenationOfAllWords.java b/算法周更班/class_2023_07_1_week/Code02_SubstringWithConcatenationOfAllWords.java new file mode 100644 index 0000000..d4d0514 --- /dev/null +++ b/算法周更班/class_2023_07_1_week/Code02_SubstringWithConcatenationOfAllWords.java @@ -0,0 +1,133 @@ +package class_2023_07_1_week; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +// 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 +// s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 +// 例如,如果 words = ["ab","cd","ef"] +// 那么 "abcdef", "abefcd","cdabef" +// "cdefab","efabcd", 和 "efcdab" 都是串联子串 +// "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。 +// 返回所有串联字串在 s 中的开始索引 +// 你可以以 任意顺序 返回答案 +// 1 <= s.length <= 10^4 +// 1 <= words.length <= 5000 +// 1 <= words[i].length <= 30 +// words[i] 和 s 由小写英文字母组成 +// 测试链接 : https://leetcode.cn/problems/substring-with-concatenation-of-all-words/ +public class Code02_SubstringWithConcatenationOfAllWords { + + // 用字符串哈希做,时间复杂度才能到最优 + // 如果s的长度为n,words里所有单词的总长度为m + // 时间复杂度O(n + m),最优解的时间复杂度与单词个数、单词长度是无关的 + // 所有题解都没有做到这个复杂度的 + // 虽然这个做法打败比例没有到100%,但那是因为数据量不够大 + // 所以最优解的时间复杂度优势没有体现出来 + // 选一个质数做进制数 + public static int BASE = 499; + + // 计算一个字符串的哈希值 + public static long hashValue(String str) { + if (str.equals("")) { + return 0; + } + int n = str.length(); + long ans = str.charAt(0) - 'a' + 1; + for (int j = 1; j < n; j++) { + ans = ans * BASE + str.charAt(j) - 'a' + 1; + } + return ans; + } + + // 字符串最大长度 + // 以下内容看字符串哈希的内容 + public static int MAXN = 10001; + + public static long[] pow = new long[MAXN]; + + static { + pow[0] = 1; + for (int j = 1; j < MAXN; j++) { + pow[j] = pow[j - 1] * BASE; + } + } + + public static long[] hash = new long[MAXN]; + + public static void buildHash(String str) { + hash[0] = str.charAt(0) - 'a' + 1; + for (int j = 1; j < str.length(); j++) { + hash[j] = hash[j - 1] * BASE + str.charAt(j) - 'a' + 1; + } + } + + // 范围是[l,r),左闭右开 + public static long hashValue(int l, int r) { + long ans = hash[r - 1]; + ans -= l == 0 ? 0 : (hash[l - 1] * pow[r - l]); + return ans; + } + + public static List findSubstring(String s, String[] words) { + List ans = new ArrayList<>(); + if (s == null || s.length() == 0 || words == null || words.length == 0) { + return ans; + } + // "abc" : 2 + // "fct" : 1 + // Long : string -> long + HashMap map = new HashMap<>(); + for (String key : words) { + long v = hashValue(key); + map.put(v, map.getOrDefault(v, 0) + 1); + } + buildHash(s); + int n = s.length(); + int wordLen = words[0].length(); + int wordNum = words.length; + int allLen = wordLen * wordNum; + HashMap window = new HashMap<>(); + for (int init = 0; init < wordLen && init + allLen <= n; init++) { + // [0...5) [5...10) [10...15) ... + // [1...6) [6...11) [11...16) ... + // [2...7) [7...12) [12...17) ... + int debt = wordNum; + // [0...5) [5...10) [10...15) + for (int l = init, r = init + wordLen, part = 0; part < wordNum; l += wordLen, r += wordLen, part++) { + long cur = hashValue(l, r); + window.put(cur, window.getOrDefault(cur, 0) + 1); + if (window.get(cur) <= map.getOrDefault(cur, 0)) { + debt--; + } + } + if (debt == 0) { + ans.add(init); + } + // [5...10) [10...15) [15...20) + // [10...15) [15...20)[20...25] + for (int l1 = init, r1 = init + wordLen, l2 = init + allLen, + r2 = init + allLen + wordLen; r2 <= n; + l1 += wordLen, r1 += wordLen, l2 += wordLen, r2 += wordLen) { + // l1...r1 ....... l2...r2 + long out = hashValue(l1, r1); + long in = hashValue(l2, r2); + window.put(out, window.get(out) - 1); + if (window.get(out) < map.getOrDefault(out, 0)) { + debt++; + } + window.put(in, window.getOrDefault(in, 0) + 1); + if (window.get(in) <= map.getOrDefault(in, 0)) { + debt--; + } + if (debt == 0) { + ans.add(r1); + } + } + window.clear(); + } + return ans; + } + +} diff --git a/算法周更班/class_2023_07_1_week/Code03_StrongestForceField.java b/算法周更班/class_2023_07_1_week/Code03_StrongestForceField.java new file mode 100644 index 0000000..794e45c --- /dev/null +++ b/算法周更班/class_2023_07_1_week/Code03_StrongestForceField.java @@ -0,0 +1,88 @@ +package class_2023_07_1_week; + +import java.util.Arrays; + +// 小扣在探索丛林的过程中,无意间发现了传说中"落寞的黄金之都" +// 而在这片建筑废墟的地带中,小扣使用探测仪监测到了存在某种带有「祝福」效果的力场 +// 经过不断的勘测记录,小扣将所有力场的分布都记录了下来 +// forceField[i] = [x,y,side] +// 表示第 i 片力场将覆盖以坐标 (x,y) 为中心,边长为 side 的正方形区域。 +// 若任意一点的 力场强度 等于覆盖该点的力场数量 +// 请求出在这片地带中 力场强度 最强处的 力场强度 +// 注意:力场范围的边缘同样被力场覆盖。 +// 测试链接 : https://leetcode.cn/problems/xepqZ5/ +public class Code03_StrongestForceField { + + public static int fieldOfGreatestBlessing(int[][] fields) { + int n = fields.length; + // n : 矩形的个数,x 2*n个坐标 + long[] xs = new long[n << 1]; + long[] ys = new long[n << 1]; + for (int i = 0, k = 0, p = 0; i < n; i++) { + long x = fields[i][0]; + long y = fields[i][1]; + long r = fields[i][2]; + xs[k++] = (x << 1) - r; + xs[k++] = (x << 1) + r; + ys[p++] = (y << 1) - r; + ys[p++] = (y << 1) + r; + } + int sizex = sort(xs); + int sizey = sort(ys); + int[][] diff = new int[sizex + 2][sizey + 2]; + for (int i = 0, a, b, c, d; i < n; i++) { + long x = fields[i][0]; + long y = fields[i][1]; + long r = fields[i][2]; + a = rank(xs, (x << 1) - r, sizex); + b = rank(ys, (y << 1) - r, sizey); + c = rank(xs, (x << 1) + r, sizex); + d = rank(ys, (y << 1) + r, sizey); + set(diff, a, b, c, d); + } + int ans = 0; + for (int i = 1; i < diff.length; i++) { + for (int j = 1; j < diff[0].length; j++) { + diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1]; + ans = Math.max(ans, diff[i][j]); + } + } + return ans; + } + + public static int sort(long[] nums) { + Arrays.sort(nums); + int size = 1; + for (int i = 1; i < nums.length; i++) { + if (nums[i] != nums[size - 1]) { + nums[size++] = nums[i]; + } + } + return size; + } + + public static int rank(long[] nums, long v, int size) { + int l = 0; + int r = size - 1; + int m, ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (nums[m] >= v) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans + 1; + } + + // 二维差分 + public static void set(int[][] diff, int a, int b, int c, int d) { + diff[a][b] += 1; + diff[c + 1][d + 1] += 1; + diff[c + 1][b] -= 1; + diff[a][d + 1] -= 1; + } + +} diff --git a/算法周更班/class_2023_07_1_week/Code04_SwapRowOrColMakeDiagonalAllOne.java b/算法周更班/class_2023_07_1_week/Code04_SwapRowOrColMakeDiagonalAllOne.java new file mode 100644 index 0000000..92cd62e --- /dev/null +++ b/算法周更班/class_2023_07_1_week/Code04_SwapRowOrColMakeDiagonalAllOne.java @@ -0,0 +1,142 @@ +package class_2023_07_1_week; + +// 来自网易 +// 题目出处 : https://leetcode.cn/circle/discuss/uOnnUA/ +// 已知一个n*n的01矩阵, +// 只能通过通过行交换、或者列交换的方式调整矩阵, +// 判断这个矩阵的对角线是否能全为1,如果能返回true,不能返回false +// 我们升级一下: +// 已知一个n*n的01矩阵, +// 只能通过通过行交换、或者列交换的方式调整矩阵, +// 判断这个矩阵的对角线是否能全为1,如果不能打印-1 +// 如果能,打印需要交换的次数,并且打印怎么交换 +// 测试链接 : http://acm.hdu.edu.cn/showproblem.php?pid=2819 +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交以下的所有代码,并把主类名改成"Main" +// 可以直接通过 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_SwapRowOrColMakeDiagonalAllOne { + + public static int[][] out = new int[1000][2]; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer pin = new StreamTokenizer(br); + PrintWriter pout = new PrintWriter(new OutputStreamWriter(System.out)); + while (pin.nextToken() != StreamTokenizer.TT_EOF) { + int n = (int) pin.nval; + int[][] graph = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + pin.nextToken(); + graph[i][j] = (int) pin.nval; + } + } + int t = km(graph); + pout.println(t); + for (int i = 0; i < t; i++) { + pout.println("R " + (out[i][0] + 1) + " " + (out[i][1] + 1)); + } + pout.flush(); + } + } + + // 改写的km算法 + // 如果达成的最优匹配收益 < N,返回-1; + // 如果达成的最优匹配收益 == N,加工好如何交换的结果(全局out数组), + // 最终返回交换的次数(>=0); + public static int km(int[][] graph) { + int N = graph.length; + int[] lx = new int[N]; + int[] ly = new int[N]; + int[] match = new int[N]; + boolean[] x = new boolean[N]; + boolean[] y = new boolean[N]; + int[] slack = new int[N]; + int invalid = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + match[i] = -1; + lx[i] = -invalid; + for (int j = 0; j < N; j++) { + lx[i] = Math.max(lx[i], graph[i][j]); + } + ly[i] = 0; + } + for (int from = 0; from < N; from++) { + for (int i = 0; i < N; i++) { + slack[i] = invalid; + } + Arrays.fill(x, false); + Arrays.fill(y, false); + while (!dfs(from, x, y, lx, ly, match, slack, graph)) { + int d = invalid; + for (int i = 0; i < N; i++) { + if (!y[i] && slack[i] < d) { + d = slack[i]; + } + } + for (int i = 0; i < N; i++) { + if (x[i]) { + lx[i] = lx[i] - d; + } + if (y[i]) { + ly[i] = ly[i] + d; + } + } + Arrays.fill(x, false); + Arrays.fill(y, false); + } + } + int ans = 0; + for (int i = 0; i < N; i++) { + ans += (lx[i] + ly[i]); + } + if (ans < N) { + return -1; + } + int t = 0; + for (int i = 0; i < N; i++) { + int u = match[i], v = i; + if (u != v) { + out[t][0] = v; + out[t++][1] = u; + for (int j = i + 1; j < N; j++) { + if (match[j] == v) { + match[j] = u; + } + } + } + } + return t; + } + + public static boolean dfs(int from, boolean[] x, boolean[] y, int[] lx, int[] ly, int[] match, int[] slack, + int[][] map) { + int N = map.length; + x[from] = true; + for (int to = 0; to < N; to++) { + if (!y[to]) { + int d = lx[from] + ly[to] - map[from][to]; + if (d != 0) { + slack[to] = Math.min(slack[to], d); + } else { + y[to] = true; + if (match[to] == -1 || dfs(match[to], x, y, lx, ly, match, slack, map)) { + match[to] = from; + return true; + } + } + } + } + return false; + } + +} \ No newline at end of file diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 715af3a..3eebdc7 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -4013,6 +4013,66 @@ Oliver 想要你告诉他,他们最少要花费多少时间,才能使所有 +第075节 2023年7月1周流行算法题目解析 + +先来一个最近国外同学考的题目 +已知一些供应点的位置,一共n个供应点 +其中有n-1个供应点一定都在x轴上,比如(15,0)位置,(2,0)位置等 +只有1个供应点不在x轴上,比如(23,17)位置 +给出每个供应点的位置,并且给定第k号供应点是出发点 +要求每个供应点最多走过2次,返回从k点出发,走完所有供应点的最少距离 +上面这个题没有代码实现 +因为这个题就是彻底的业务分析,只有一系列的贪心设计,代码也不难写 +同时这个题没有给出数据量,但是课上会讲O(n)的方法,所以也就无所谓了 +以下是这节课的正式题,来自学员问题 +现在有一个打怪类型的游戏,这个游戏是这样的,你有n个技能 +每一个技能会有一个伤害, +同时若怪物小于等于一定的血量,则该技能可能造成双倍伤害 +每一个技能最多只能释放一次,已知怪物有m点血量 +现在想问你最少用几个技能能消灭掉他(血量小于等于0) +技能的数量是n,怪物的血量是m +i号技能的伤害是x[i],i号技能触发双倍伤害的血量最小值是y[i] +1 <= n <= 10 +1 <= m、x[i]、y[i] <= 10^6 +测试链接 : https://www.nowcoder.com/questionTerminal/d88ef50f8dab4850be8cd4b95514bbbd + +给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 +s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 +例如,如果 words = ["ab","cd","ef"] +那么 "abcdef", "abefcd","cdabef" +"cdefab","efabcd", 和 "efcdab" 都是串联子串 +"acdbef" 不是串联子串,因为他不是任何 words 排列的连接。 +返回所有串联字串在 s 中的开始索引 +你可以以 任意顺序 返回答案 +1 <= s.length <= 10^4 +1 <= words.length <= 5000 +1 <= words[i].length <= 30 +words[i] 和 s 由小写英文字母组成 +测试链接 : https://leetcode.cn/problems/substring-with-concatenation-of-all-words/ + +小扣在探索丛林的过程中,无意间发现了传说中"落寞的黄金之都" +而在这片建筑废墟的地带中,小扣使用探测仪监测到了存在某种带有「祝福」效果的力场 +经过不断的勘测记录,小扣将所有力场的分布都记录了下来 +forceField[i] = [x,y,side] +表示第 i 片力场将覆盖以坐标 (x,y) 为中心,边长为 side 的正方形区域。 +若任意一点的 力场强度 等于覆盖该点的力场数量 +请求出在这片地带中 力场强度 最强处的 力场强度 +注意:力场范围的边缘同样被力场覆盖。 +测试链接 : https://leetcode.cn/problems/xepqZ5/ + +来自网易 +题目出处 : https://leetcode.cn/circle/discuss/uOnnUA/ +已知一个n*n的01矩阵, +只能通过通过行交换、或者列交换的方式调整矩阵, +判断这个矩阵的对角线是否能全为1,如果能返回true,不能返回false +我们升级一下: +已知一个n*n的01矩阵, +只能通过通过行交换、或者列交换的方式调整矩阵, +判断这个矩阵的对角线是否能全为1,如果不能打印-1 +如果能,打印需要交换的次数,并且打印怎么交换 +测试链接 : http://acm.hdu.edu.cn/showproblem.php?pid=2819 + +