diff --git a/算法周更班/class_2023_02_4_week/Code01_MaximumAveragePassRatio.java b/算法周更班/class_2023_02_4_week/Code01_MaximumAveragePassRatio.java new file mode 100644 index 0000000..22b9feb --- /dev/null +++ b/算法周更班/class_2023_02_4_week/Code01_MaximumAveragePassRatio.java @@ -0,0 +1,56 @@ +package class_2023_02_4_week; + +import java.util.PriorityQueue; + +// 一所学校里有一些班级,每个班级里有一些学生,现在每个班都会进行一场期末考试 +// 给你一个二维数组 classes ,其中 classes[i] = [passi, totali] +// 表示你提前知道了第 i 个班级总共有 totali 个学生,其中只有 passi 个学生可以通过考试 +// 给你一个整数 extraStudents ,表示额外有 extraStudents 个聪明的学生 +// 他们 一定 能通过任何班级的期末考 +// 你需要给这 extraStudents 个学生每人都安排一个班级 +// 使得 所有 班级的 平均 通过率 最大 。 +// 一个班级的 通过率 等于这个班级通过考试的学生人数除以这个班级的总人数 +// 平均通过率 是所有班级的通过率之和除以班级数目。 +// 请你返回在安排这 extraStudents 个学生去对应班级后的 最大 平均通过率 +// 与标准答案误差范围在 10^-5 以内的结果都会视为正确结果。 +// 测试链接 : https://leetcode.cn/problems/maximum-average-pass-ratio/ +public class Code01_MaximumAveragePassRatio { + + public static double maxAverageRatio(int[][] classes, int extraStudents) { + // 堆 : 谁获得一个天才,得到的通过率增益最大 + // 谁先弹出 + PriorityQueue heap = new PriorityQueue<>((a, b) -> a.benefit() - b.benefit() < 0 ? 1 : -1); + for (int[] p : classes) { + heap.add(new Party(p[0], p[1])); + } + Party cur; + // 一个一个天才分配 + for (int i = 0; i < extraStudents; i++) { + cur = heap.poll(); + cur.pass++; + cur.total++; + heap.add(cur); + } + double all = 0; + while (!heap.isEmpty()) { + cur = heap.poll(); + all += cur.pass / cur.total; + } + return all / classes.length; + } + + public static class Party { + public double pass; + public double total; + + public Party(int p, int t) { + pass = p; + total = t; + } + + public double benefit() { + return (pass + 1) / (total + 1) - pass / total; + } + } + +} diff --git a/算法周更班/class_2023_02_4_week/Code02_SplitNumberTimesMax.java b/算法周更班/class_2023_02_4_week/Code02_SplitNumberTimesMax.java new file mode 100644 index 0000000..8a42418 --- /dev/null +++ b/算法周更班/class_2023_02_4_week/Code02_SplitNumberTimesMax.java @@ -0,0 +1,65 @@ +package class_2023_02_4_week; + +// 来自学员问题 +// 给你一根长度为 n 的绳子 +// 请把绳子剪成整数长度的 m 段 +// m、n都是整数,n > 1并且m > 1 +// 每段绳子的长度记为 k[0],k[1]...k[m - 1] +// 请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少 +// 例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18 +// 答案需要取模1000000007 +// 测试链接 : https://leetcode.cn/problems/jian-sheng-zi-ii-lcof/ +public class Code02_SplitNumberTimesMax { + + public static int mod = 1000000007; + + // x的n次方,% mod之后,是多少? + // 快速幂的方式,体系学习班,斐波那契数列章节 + public static long power(long x, int n) { + long ans = 1; + while (n > 0) { + if ((n & 1) == 1) { + ans = (ans * x) % mod; + } + x = (x * x) % mod; + n >>= 1; + } + return ans; + } + + // 纯观察,没有为什么 + public static int cuttingRope(int n) { + if (n == 2) { + return 1; + } + if (n == 3) { + return 2; + } + // n >= 4 + // n = 13 + // n % 3 == 1 + // 4 -> 2 * 2 (n-4) / 3 -> 3的(n-4)/3 次方 + // n % 3 == 2 + // 2 -> 2 (n-2)/3 -> 3的(n-2)/3次方 + // n: + // + // last : + // n = 9 + // 3 * 3 * 3 * 1 + // n = 10 + // 3 * 3 * (2 * 2) + // n = 11 + // 3 * 3 * 3 * (2) + // n = 9 + // rest = 9 -> 3 ? + // n = 10 + // rest = 10 - 4 -> 6 ? + // n == 11 + // rest = 11 - 2 = 9 ? + int rest = n % 3 == 0 ? n : (n % 3 == 1 ? (n - 4) : (n - 2)); + int last = n % 3 == 0 ? 1 : (n % 3 == 1 ? 4 : 2); + // (3的(rest/3)次方 * last) % mod + return (int) ((power(3, rest / 3) * last) % mod); + } + +} diff --git a/算法周更班/class_2023_02_4_week/Code03_GridIllumination.java b/算法周更班/class_2023_02_4_week/Code03_GridIllumination.java new file mode 100644 index 0000000..c320e89 --- /dev/null +++ b/算法周更班/class_2023_02_4_week/Code03_GridIllumination.java @@ -0,0 +1,93 @@ +package class_2023_02_4_week; + +import java.util.HashMap; +import java.util.HashSet; + +// 在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态 +// 给你一个由灯的位置组成的二维数组 lamps +// 其中 lamps[i] = [rowi, coli] 表示 打开 位于 grid[rowi][coli] 的灯 +// 即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态 +// 当一盏灯处于打开状态,它将会照亮 自身所在单元格 +// 以及同一 行 、同一 列 和两条 对角线 上的 所有其他单元格 +// 另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] +// 对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的 +// 则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] +// 关闭 位于单元格 grid[rowj][colj] 上 +// 及相邻 8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯 +// 返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果 +// 1 表示照亮,0 表示未照亮 +// 测试链接 : https://leetcode.cn/problems/grid-illumination/ +public class Code03_GridIllumination { + + public static int[][] move = { + { 0, 0 }, + { 0, -1 }, + { 0, 1 }, + { -1, 0 }, + { -1, -1 }, + { -1, 1 }, + { 1, 0 }, + { 1, -1 }, + { 1, 1 } }; + + // n -> 大区域是 n * n + // lamps + // queries + public static int[] gridIllumination(int n, int[][] lamps, int[][] queries) { + long limit = n; + HashMap row = new HashMap<>(); + HashMap col = new HashMap<>(); + HashMap leftUpDiag = new HashMap<>(); + HashMap rightUpDiag = new HashMap<>(); + // (x,y) -> x*列的数量 + y -> 得到的数字代表一个点 + HashSet points = new HashSet<>(); + for (int[] p : lamps) { + // 所有的灯,注册! + // 如果之前加过,add方法返回fasle + // 如果之前没加过,add方法返回true + if (points.add(limit * p[0] + p[1])) { + row.put(p[0], row.getOrDefault(p[0], 0) + 1); + col.put(p[1], col.getOrDefault(p[1], 0) + 1); + leftUpDiag.put(p[0] - p[1], leftUpDiag.getOrDefault(p[0] - p[1], 0) + 1); + rightUpDiag.put(p[0] + p[1], rightUpDiag.getOrDefault(p[0] + p[1], 0) + 1); + } + } + int[] ans = new int[queries.length]; + int ansi = 0; + for (int[] q : queries) { + // q[0], q[1] + ans[ansi++] = (row.containsKey(q[0]) + || col.containsKey(q[1]) + || leftUpDiag.containsKey(q[0] - q[1]) + || rightUpDiag.containsKey(q[0] + q[1])) ? 1 : 0; + for (int[] m : move) { + int r = q[0] + m[0]; + int c = q[1] + m[1]; + // (r,c)位置,有灯就关,没灯算了! + int lu = r - c; + int ru = r + c; + if (r < 0 || r >= n || c < 0 || c >= n) { + continue; + } + // r,c -> 列数 * r + c + if (points.contains(limit * r + c)) { + points.remove(limit * r + c); + minusOrRemove(row, r); + minusOrRemove(col, c); + minusOrRemove(leftUpDiag, lu); + minusOrRemove(rightUpDiag, ru); + } + } + } + return ans; + } + + public static void minusOrRemove(HashMap map, int key) { + if (map.get(key) == 1) { + map.remove(key); + } else { + map.put(key, map.get(key) - 1); + } + } + +} diff --git a/算法周更班/class_2023_02_4_week/Code04_StampingTheSequence.java b/算法周更班/class_2023_02_4_week/Code04_StampingTheSequence.java new file mode 100644 index 0000000..1c359ea --- /dev/null +++ b/算法周更班/class_2023_02_4_week/Code04_StampingTheSequence.java @@ -0,0 +1,82 @@ +package class_2023_02_4_week; + +import java.util.ArrayList; +import java.util.Arrays; + +// 你想要用小写字母组成一个目标字符串 target。 +// 开始的时候,序列由 target.length 个 '?' 记号组成 +// 而你有一个小写字母印章 stamp。 +// 在每个回合,你可以将印章放在序列上,并将序列中的每个字母替换为印章上的相应字母 +// 你最多可以进行 10 * target.length 个回合 +// 举个例子,如果初始序列为 "?????",而你的印章 stamp 是 "abc" +// 那么在第一回合,你可以得到 "abc??"、"?abc?"、"??abc" +//(请注意,印章必须完全包含在序列的边界内才能盖下去。) +// 如果可以印出序列,那么返回一个数组,该数组由每个回合中被印下的最左边字母的索引组成 +// 如果不能印出序列,就返回一个空数组。 +// 例如,如果序列是 "ababc",印章是 "abc" +// 那么我们就可以返回与操作 "?????" -> "abc??" -> "ababc" 相对应的答案 [0, 2] +// 另外,如果可以印出序列,那么需要保证可以在 10 * target.length 个回合内完成 +// 任何超过此数字的答案将不被接受 +// 测试链接 : https://leetcode.cn/problems/stamping-the-sequence/ +public class Code04_StampingTheSequence { + + public static int[] movesToStamp(String stamp, String target) { + char[] s = stamp.toCharArray(); + char[] t = target.toCharArray(); + int m = s.length; + int n = t.length; + int[] inDegrees = new int[n - m + 1]; + // 所有在target里开头的字符串,认为和stamp,每个位置都不一样 + Arrays.fill(inDegrees, m); + ArrayList> graph = new ArrayList<>(); + for (int i = 0; i < n; i++) { + graph.add(new ArrayList<>()); + } + // 入度为0的开头,进入到queue里 + int[] queue = new int[n - m + 1]; + int l = 0; + int r = 0; + for (int i = 0; i <= n - m; i++) { + // i....撸m个字符 + for (int j = 0; j < m; j++) { + if (t[i + j] == s[j]) { + if (--inDegrees[i] == 0) { + queue[r++] = i; + } + } else { + graph.get(i + j).add(i); + } + } + } + // visited[17] == true + boolean[] visited = new boolean[n]; + int[] path = new int[n - m + 1]; + int size = 0; + while (l < r) { + // cur位置开头的字符串,弹出了!入度为0!恭喜啊!!!! + int cur = queue[l++]; + path[size++] = cur; + for (int i = 0; i < m; i++) { + if (!visited[cur + i]) { + visited[cur + i] = true; + for (int next : graph.get(cur + i)) { + if (--inDegrees[next] == 0) { + queue[r++] = next; + } + } + } + } + } + if (size != n - m + 1) { + return new int[0]; + } + // path里 + for (int i = 0, j = size - 1; i < j; i++, j--) { + int tmp = path[i]; + path[i] = path[j]; + path[j] = tmp; + } + return path; + } + +} diff --git a/算法周更班/class_2023_02_4_week/Code05_NumberOfWaysOfCuttingPizza.java b/算法周更班/class_2023_02_4_week/Code05_NumberOfWaysOfCuttingPizza.java new file mode 100644 index 0000000..c589d9f --- /dev/null +++ b/算法周更班/class_2023_02_4_week/Code05_NumberOfWaysOfCuttingPizza.java @@ -0,0 +1,201 @@ +package class_2023_02_4_week; + +// 给你一个 rows * cols 大小的矩形披萨和一个整数 k +// 矩形包含两种字符: 'A' (表示苹果)和 '.' (表示空白格子) +// 你需要切披萨 k-1 次,得到 k 块披萨并送给别人 +// 切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置 +// 将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人 +// 如果水平地切,那么需要把上面的部分送给一个人 +// 在切完最后一刀后,需要把剩下来的一块送给最后一个人 +// 请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数 +// 由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果 +// 测试链接 : https://leetcode.cn/problems/number-of-ways-of-cutting-a-pizza/ +public class Code05_NumberOfWaysOfCuttingPizza { + + // 暴力方法 + public static int ways1(String[] pizza, int k) { + int n = pizza.length; + int m = pizza[0].length(); + int[][] sum = new int[n + 1][m + 1]; + setAppleMatrix(pizza, sum, n, m); + return process(sum, n, m, 1, 1, k); + } + + // 暴力方法 + // sum : 帮助你查询(a,b)做左上角,(c,d)做右下角,有几个苹果,快速查询! O(1) + // 披萨饼的整体规模 : n * m + // 剩余没切的披萨饼, 左上角点(row, col),右下角点(n, m) + // 剩余的区域,一定要切出rest份! + // 返回有多少种方法! + public static int process(int[][] sum, int n, int m, int row, int col, int rest) { + if (apple(sum, row, col, n, m) == 0) { + return 0; + } + // 剩余区域上,有苹果 + if (rest == 1) { + return 1; + } + int ways = 0; + for (int i = col; i < m; i++) { + if (apple(sum, row, col, n, i) > 0) { + ways += process(sum, n, m, row, i + 1, rest - 1); + ways %= mod; + } + } + for (int i = row; i < n; i++) { + if (apple(sum, row, col, i, m) > 0) { + ways += process(sum, n, m, i + 1, col, rest - 1); + ways %= mod; + } + } + return ways; + } + + // 暴力方法改动态规划 + 小优化 + // 时间复杂度O(N * M * K * (N + M)) + public static int ways2(String[] pizza, int k) { + int n = pizza.length; + int m = pizza[0].length(); + int[][] sum = new int[n + 1][m + 1]; + setAppleMatrix(pizza, sum, n, m); + int[][][] dp = new int[k + 1][n + 1][m + 1]; + for (int r = 1; r <= n; r++) { + for (int c = 1; c <= m; c++) { + if (apple(sum, r, c, n, m) > 0) { + dp[1][r][c] = 1; + } + } + } + for (int level = 2; level <= k; level++) { + for (int row = n; row >= 1; row--) { + for (int col = m; col >= 1; col--) { + int ways = 0; + for (int c = col; c < m; c++) { + if (apple(sum, row, col, n, c) > 0) { + for (int s = c + 1; s <= m; s++) { + ways += dp[level - 1][row][s]; + ways %= mod; + } + break; + } + } + for (int r = row; r < n; r++) { + if (apple(sum, row, col, r, m) > 0) { + for (int s = r + 1; s <= n; s++) { + ways += dp[level - 1][s][col]; + ways %= mod; + } + break; + } + } + dp[level][row][col] = ways; + } + } + } + return dp[k][1][1]; + } + + // 动态规划 + 观察位置依赖的大优化 + // 基本上LeetCode上面题解都没有做到这个复杂度的 + // 时间复杂度O(N * M * K) + public static int ways3(String[] pizza, int k) { + int n = pizza.length; + int m = pizza[0].length(); + int[][] sum = new int[n + 1][m + 1]; + setAppleMatrix(pizza, sum, n, m); + int[][] nearr = new int[n + 1][m + 1]; + int[][] nearc = new int[n + 1][m + 1]; + setNear(sum, nearr, nearc, n, m); + int[][] dp = new int[n + 1][m + 1]; + int[][] rs = new int[n + 1][m + 1]; + int[][] cs = new int[n + 1][m + 1]; + for (int r = 1; r <= n; r++) { + for (int c = 1; c <= m; c++) { + if (apple(sum, r, c, n, m) > 0) { + dp[r][c] = 1; + } + } + } + setRowColSums(dp, rs, cs, n, m); + for (int level = 2; level <= k; level++) { + for (int r = 1; r <= n; r++) { + for (int c = 1; c <= m; c++) { + // (r,c) 剩余区域,离的最近的苹果,在哪一列 + // (r,c) 剩余区域,离的最近的苹果,在哪一行 + dp[r][c] = nearr[r][c] < n ? (rs[nearr[r][c] + 1][c]) : 0; + dp[r][c] = (dp[r][c] + (nearc[r][c] < m ? cs[r][nearc[r][c] + 1] : 0)) % mod; + } + } + // 并列关系! + // 又来两个for循环!!!! + setRowColSums(dp, rs, cs, n, m); + } + return dp[1][1]; + } + + public static int mod = 1000000007; + + public static void setAppleMatrix(String[] pizza, int[][] sum, int n, int m) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + sum[i + 1][j + 1] = pizza[i].charAt(j) == 'A' ? 1 : 0; + } + } + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]; + } + } + } + + public static int apple(int[][] sum, int a, int b, int c, int d) { + return sum[c][d] - sum[c][b - 1] - sum[a - 1][d] + sum[a - 1][b - 1]; + } + + public static void setNear(int[][] sum, int[][] nearr, int[][] nearc, int n, int m) { + for (int r = 1; r <= n; r++) { + int right = m + 1; + int number = 0; + for (int c = m; c >= 1; c--) { + int curApple = apple(sum, r, c, n, m); + if (curApple > number) { + number = curApple; + right = c; + } + nearc[r][c] = right; + } + } + for (int c = 1; c <= m; c++) { + int down = n + 1; + int number = 0; + for (int r = n; r >= 1; r--) { + int curApple = apple(sum, r, c, n, m); + if (curApple > number) { + number = curApple; + down = r; + } + nearr[r][c] = down; + } + } + } + + public static void setRowColSums(int[][] dp, int[][] rs, int[][] cs, int n, int m) { + rs[n][m] = dp[n][m]; + cs[n][m] = dp[n][m]; + for (int r = n - 1; r >= 1; r--) { + cs[r][m] = dp[r][m]; + rs[r][m] = (dp[r][m] + rs[r + 1][m]) % mod; + } + for (int c = m - 1; c >= 1; c--) { + rs[n][c] = dp[n][c]; + cs[n][c] = (dp[n][c] + cs[n][c + 1]) % mod; + } + for (int r = n - 1; r >= 1; r--) { + for (int c = m - 1; c >= 1; c--) { + rs[r][c] = (dp[r][c] + rs[r + 1][c]) % mod; + cs[r][c] = (dp[r][c] + cs[r][c + 1]) % mod; + } + } + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 60fc22a..43a7556 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -3015,19 +3015,72 @@ X价值如果不是所有剩余宝石价值中的最小值,你会将该宝石 +第059节 2023年2月第4周流行算法题目解析 + +一所学校里有一些班级,每个班级里有一些学生,现在每个班都会进行一场期末考试 +给你一个二维数组 classes ,其中 classes[i] = [passi, totali] +表示你提前知道了第 i 个班级总共有 totali 个学生,其中只有 passi 个学生可以通过考试 +给你一个整数 extraStudents ,表示额外有 extraStudents 个聪明的学生 +他们 一定 能通过任何班级的期末考 +你需要给这 extraStudents 个学生每人都安排一个班级 +使得 所有 班级的 平均 通过率 最大 。 +一个班级的 通过率 等于这个班级通过考试的学生人数除以这个班级的总人数 +平均通过率 是所有班级的通过率之和除以班级数目。 +请你返回在安排这 extraStudents 个学生去对应班级后的 最大 平均通过率 +与标准答案误差范围在 10^-5 以内的结果都会视为正确结果。 +测试链接 : https://leetcode.cn/problems/maximum-average-pass-ratio/ - - - - - - - - - - - - +来自学员问题 +给你一根长度为 n 的绳子 +请把绳子剪成整数长度的 m 段 +m、n都是整数,n > 1并且m > 1 +每段绳子的长度记为 k[0],k[1]...k[m - 1] +请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少 +例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18 +答案需要取模1000000007 +测试链接 : https://leetcode.cn/problems/jian-sheng-zi-ii-lcof/ + +在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态 +给你一个由灯的位置组成的二维数组 lamps +其中 lamps[i] = [rowi, coli] 表示 打开 位于 grid[rowi][coli] 的灯 +即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态 +当一盏灯处于打开状态,它将会照亮 自身所在单元格 +以及同一 行 、同一 列 和两条 对角线 上的 所有其他单元格 +另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] +对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的 +则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] +关闭 位于单元格 grid[rowj][colj] 上 +及相邻 8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯 +返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果 +1 表示照亮,0 表示未照亮 +测试链接 : https://leetcode.cn/problems/grid-illumination/ + +你想要用小写字母组成一个目标字符串 target。 +开始的时候,序列由 target.length 个 '?' 记号组成 +而你有一个小写字母印章 stamp。 +在每个回合,你可以将印章放在序列上,并将序列中的每个字母替换为印章上的相应字母 +你最多可以进行 10 * target.length 个回合 +举个例子,如果初始序列为 "?????",而你的印章 stamp 是 "abc" +那么在第一回合,你可以得到 "abc??"、"?abc?"、"??abc" +(请注意,印章必须完全包含在序列的边界内才能盖下去。) +如果可以印出序列,那么返回一个数组,该数组由每个回合中被印下的最左边字母的索引组成 +如果不能印出序列,就返回一个空数组。 +例如,如果序列是 "ababc",印章是 "abc" +那么我们就可以返回与操作 "?????" -> "abc??" -> "ababc" 相对应的答案 [0, 2] +另外,如果可以印出序列,那么需要保证可以在 10 * target.length 个回合内完成 +任何超过此数字的答案将不被接受 +测试链接 : https://leetcode.cn/problems/stamping-the-sequence/ + +给你一个 rows * cols 大小的矩形披萨和一个整数 k +矩形包含两种字符: 'A' (表示苹果)和 '.' (表示空白格子) +你需要切披萨 k-1 次,得到 k 块披萨并送给别人 +切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置 +将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人 +如果水平地切,那么需要把上面的部分送给一个人 +在切完最后一刀后,需要把剩下来的一块送给最后一个人 +请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数 +由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果 +测试链接 : https://leetcode.cn/problems/number-of-ways-of-cutting-a-pizza/