diff --git a/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java b/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java index b30658b..cc5cd32 100644 --- a/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java +++ b/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java @@ -25,7 +25,7 @@ public class Code04_SumEvenSubNumber { } int n = arr.length; // even[i][j] : 在前i个数的范围上(0...i-1),一定选j个数,加起来是偶数的子序列个数 - // odd[i][j] : 在前i个数的范围上(0...i-1),一定选j个数,加起来是奇数的子序列个数 + // odd[i][j] : 在前i个数的范围上(0...i-1),一定选j个数,加起来是奇数的子序列个数 int[][] even = new int[n + 1][k + 1]; int[][] odd = new int[n + 1][k + 1]; for (int i = 0; i <= n; i++) { @@ -46,6 +46,56 @@ public class Code04_SumEvenSubNumber { return even[n][k]; } + // 补充一个更数学的方法 + // 统计arr中的偶数个数、奇数个数 + // k个数加起来是偶数的方案 : + // 1) 奇数选0个,偶数选k个 + // 2) 奇数选2个,偶数选k-2个 + // 3) 奇数选4个,偶数选k-4个 + // ... + public static int number3(int[] arr, int k) { + if (arr == null || arr.length == 0 || k < 1 || k > arr.length) { + return 0; + } + int even = 0; + int odd = 0; + for (int num : arr) { + if ((num & 1) == 0) { + even++; + } else { + odd++; + } + } + int ans = 0; + for (int pick = 0, rest = k; pick <= k; pick += 2, rest -= 2) { + ans += c(pick, odd) * c(rest, even); + } + return ans; + } + + public static long c(long m, long n) { + if (m > n) { + return 0; + } + if (m == 0 && m == n) { + return 1; + } + long up = 1; + long down = 1; + for (long i = m + 1, j = 1; i <= n; i++, j++) { + up *= i; + down *= j; + long gcd = gcd(up, down); + up /= gcd; + down /= gcd; + } + return up; + } + + public static long gcd(long m, long n) { + return n == 0 ? m : gcd(n, m % n); + } + // 为了测试 public static int[] randomArray(int n, int v) { int[] ans = new int[n]; @@ -67,7 +117,8 @@ public class Code04_SumEvenSubNumber { int k = (int) (Math.random() * n) + 1; int ans1 = number1(arr, k); int ans2 = number2(arr, k); - if (ans1 != ans2) { + int ans3 = number3(arr, k); + if (ans1 != ans2 || ans1 != ans3) { System.out.println("出错了"); for (int num : arr) { System.out.print(num + " "); @@ -76,6 +127,7 @@ public class Code04_SumEvenSubNumber { System.out.println(k); System.out.println(ans1); System.out.println(ans2); + System.out.println(ans3); } } System.out.println("测试结束"); diff --git a/算法周更班/class_2022_09_4_week/Code01_RobotDeliverGoods.java b/算法周更班/class_2022_09_4_week/Code01_RobotDeliverGoods.java new file mode 100644 index 0000000..455122f --- /dev/null +++ b/算法周更班/class_2022_09_4_week/Code01_RobotDeliverGoods.java @@ -0,0 +1,176 @@ +package class_2022_09_4_week; + +// 来自学员问题 +// 智能机器人要坐专用电梯把货物送到指定地点 +// 整栋楼只有一部电梯,并且由于容量限制智能机器人只能放下一件货物 +// 给定K个货物,每个货物都有所在楼层(from)和目的楼层(to) +// 假设电梯速度恒定为1,相邻两个楼层之间的距离为1 +// 例如电梯从10层去往19层的时间为9 +// 机器人装卸货物的时间极快不计入 +// 电梯初始地点为第1层,机器人初始地点也是第1层 +// 并且在运送完所有货物之后,机器人和电梯都要回到1层 +// 返回智能机器人用电梯将每个物品都送去目标楼层的最快时间 +// 注意:如果智能机器人选了一件物品,则必须把这个物品送完,不能中途丢下 +// 输入描述: +// 正数k,表示货物数量,1 <= k <= 16 +// from、to数组,长度都是k,1 <= from[i]、to[i] <= 10000 +// from[i]表示i号货物所在的楼层 +// to[i]表示i号货物要去往的楼层 +// 返回最快的时间 +public class Code01_RobotDeliverGoods { + // 0 1 2 + // from = {3, 6, 2} + // to = {7, 9, 1} + // from[i] : 第i件货,在哪个楼层拿货,固定 + // to[i] : 第i件货,去哪个楼层送货,固定 + // k : 一共有几件货,固定 + // status : 00110110 + // last : 在送过的货里,最后送的是第几号货物 + // 返回值: 送完的货,由status代表, + // 而且last是送完的货里的最后一件,后续所有没送过的货都送完, + // 最后回到第一层,返回最小耗时 + // k = 7 + // status = 01111111 + // 0000000000001 + // 0000010000000 -1 + // 0000001111111 + public static int zuo(int status, int last, int k, int[] from, int[] to) { + if (status == (1 << k) - 1) { // 所有货送完了,回到1层去了 + return to[last] - 1; + } else { // 不是所有货都送完! + int ans = Integer.MAX_VALUE; + for (int cur = 0; cur < k; cur++) { + // status : 0010110 + // 1 + if ( (status & (1 << cur)) == 0) { // 当前cur号的货物,可以去尝试 + // to[last] + // cur号的货,to[last] -> from[cur] + int come = Math.abs(to[last] - from[cur]); + int delive = Math.abs(to[cur] - from[cur]); + int next = zuo(status | (1 << cur), cur, k, from, to); + int curAns = come + delive + next; + ans = Math.min(ans, curAns); + } + } + return ans; + } + } + + // 暴力方法 + // 全排序代码 + public static int minCost1(int k, int[] from, int[] to) { + return process(0, k, from, to); + } + + public static int process(int i, int k, int[] from, int[] to) { + if (i == k) { + int ans = 0; + int cur = 1; + for (int j = 0; j < k; j++) { + ans += Math.abs(from[j] - cur); + ans += Math.abs(to[j] - from[j]); + cur = to[j]; + } + return ans + cur - 1; + } else { + int ans = Integer.MAX_VALUE; + for (int j = i; j < k; j++) { + swap(from, to, i, j); + ans = Math.min(ans, process(i + 1, k, from, to)); + swap(from, to, i, j); + } + return ans; + } + } + + public static void swap(int[] from, int[] to, int i, int j) { + int tmp = from[i]; + from[i] = from[j]; + from[j] = tmp; + tmp = to[i]; + to[i] = to[j]; + to[j] = tmp; + } + + // 正式方法 + public static int minCost2(int k, int[] from, int[] to) { + int m = 1 << k; + int[][] dp = new int[m][k]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < k; j++) { + dp[i][j] = -1; + } + } + return f(0, 0, k, from, to, dp); + } + + // 2^16 = 65536 * 16 = 1048576 + // 1048576 * 16 = 16777216 + public static int f(int status, int i, int k, int[] from, int[] to, int[][] dp) { + if (dp[status][i] != -1) { + return dp[status][i]; + } + int ans = Integer.MAX_VALUE; + if (status == (1 << k) - 1) { + ans = to[i] - 1; + } else { + for (int j = 0; j < k; j++) { + if ((status & (1 << j)) == 0) { + int come = Math.abs(from[j] - (status == 0 ? 1 : to[i])); + int deliver = Math.abs(to[j] - from[j]); + int next = f(status | (1 << j), j, k, from, to, dp); + ans = Math.min(ans, come + deliver + next); + } + } + } + dp[status][i] = ans; + return ans; + } + + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v) + 1; + } + return ans; + } + + public static void main(String[] args) { + int k = 5; + int[] from = { 1, 3, 6, 5, 7 }; + int[] to = { 4, 6, 3, 2, 8 }; + System.out.println(minCost1(k, from, to)); + System.out.println(minCost2(k, from, to)); + + int N = 8; + int V = 100; + int testTimes = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTimes; i++) { + k = (int) (Math.random() * N) + 1; + from = randomArray(k, V); + to = randomArray(k, V); + int ans1 = minCost1(k, from, to); + int ans2 = minCost2(k, from, to); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + k = 16; + V = 10000; + from = randomArray(k, V); + to = randomArray(k, V); + System.out.println("货物数量 : " + k); + System.out.println("楼层范围 : " + V); + long start = System.currentTimeMillis(); + minCost2(k, from, to); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + + } + +} diff --git a/算法周更班/class_2022_09_4_week/Code02_SetAllOneMinTimes.java b/算法周更班/class_2022_09_4_week/Code02_SetAllOneMinTimes.java new file mode 100644 index 0000000..f838abf --- /dev/null +++ b/算法周更班/class_2022_09_4_week/Code02_SetAllOneMinTimes.java @@ -0,0 +1,262 @@ +package class_2022_09_4_week; + +// 来自华为 +// 一个n*n的二维数组中,只有0和1两种值 +// 当你决定在某个位置操作一次 +// 那么该位置的行和列整体都会变成1,不管之前是什么状态 +// 返回让所有值全变成1,最少的操作次数 +// 1 < n < 10,没错!原题就是说n < 10, 不会到10!最多到9! +public class Code02_SetAllOneMinTimes { + +// public static int minTimes(int[][] matrix) { +// int n = matrix.length; +// int m = matrix[0].length; +// int[] arr = new int[n]; +// for (int i = 0; i < n; i++) { +// int status = 0; +// for (int j = 0; j < m; j++) { +// // 0列 1列 2列 3列 +// // 1 0 1 1 +// // 1101 +// if (matrix[i][j] == 1) { +// status |= 1 << m; +// } +// } +// arr[i] = status; +// } +// +// } +// +// // arr替代了原来的二维数组! +// // n行,m列,固定 +// // 目前来到i行、j列 +// // rowStatus : 之前哪些行点过鼠标 +// // colStatus : 之前哪些列点过鼠标 +// // i 行变化 : 9 * 9 * 2^9 * 2^9 * 9 = 191,102,976 +// public static int zuo(int[] arr, int n, int m, int i, int j, int clickRows, int clickCols) { +// if (i == n) { +// for (int row = 0; row < n; row++) { +// if ((clickRows & (1 << row)) != 0) { +// continue; +// } +// // row行,没点过鼠标 +// int merge = arr[row] | clickCols; +// int full = (1 << m) - 1; +// if (merge != full) { +// return Integer.MAX_VALUE;// 表示无效 +// } +// } +// return 0; +// } +// if (j == m) { +// return zuo(arr, n, m, i + 1, 0, clickRows, clickCols); +// } +// // i,j 正常的行、正常的列 +// // 当前就是不点鼠标 +// int p1 = zuo(arr, n, m, i, j + 1, clickRows, clickCols); +// // 当前就是点鼠标 +// int p2 = Integer.MAX_VALUE; +// int next = zuo(arr, n, m, i, j + 1, +// clickRows | (1 << i), clickCols | (1 << j)); +// if(next != Integer.MAX_VALUE) { +// p2 = 1 + next; +// } +// return Math.min(p1, p2); +// } + + // 暴力方法 + // 为了验证 + public static int setOneMinTimes1(int[][] matrix) { + int n = matrix.length; + int m = matrix[0].length; + int limit = 1 << (n * m); + int ans = Integer.MAX_VALUE; + // 0000000000000 + // 0000000000001 + // 0000000000010 + // 0000000000011 + // 0000000000100 + for (int status = 0; status < limit; status++) { + if (ok(status, matrix, n, m)) { + ans = Math.min(ans, hammingWeight(status)); + } + } + return ans; + } + + public static boolean ok(int status, int[][] matrix, int n, int m) { + int[][] help = new int[n][m]; + int limit = n * m; + for (int i = 0; i < limit; i++) { + if ((status & (1 << i)) != 0) { + int row = i / m; + int col = i % m; + for (int j = 0; j < n; j++) { + help[j][col] = 1; + } + for (int j = 0; j < m; j++) { + help[row][j] = 1; + } + } + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (help[i][j] == 0 && matrix[i][j] == 0) { + return false; + } + } + } + return true; + } + + public static int hammingWeight(int n) { + n = (n & 0x55555555) + ((n >>> 1) & 0x55555555); + n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); + n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f); + n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff); + n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff); + return n; + } + + // 正式方法 + public static int setOneMinTimes2(int[][] matrix) { + int n = matrix.length; + int m = matrix[0].length; + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + int status = 0; + for (int j = 0; j < m; j++) { + if (matrix[i][j] == 1) { + status |= 1 << j; + } + } + arr[i] = status; + } + int[][][][] dp = new int[1 << n][1 << m][n][m]; + for (int a = 0; a < (1 << n); a++) { + for (int b = 0; b < (1 << m); b++) { + for (int c = 0; c < n; c++) { + for (int d = 0; d < m; d++) { + dp[a][b][c][d] = -1; + } + } + } + } + return process2(arr, n, m, 0, 0, 0, 0, dp); + } + + public static int process2(int[] arr, int n, int m, int row, int col, int r, int c, int[][][][] dp) { + if (r == n) { + for (int i = 0; i < n; i++) { + if ((row & (1 << i)) == 0 && (arr[i] | col) != (1 << m) - 1) { + return Integer.MAX_VALUE; + } + } + return 0; + } + if (c == m) { + return process2(arr, n, m, row, col, r + 1, 0, dp); + } + if (dp[row][col][r][c] != -1) { + return dp[row][col][r][c]; + } + int p1 = process2(arr, n, m, row, col, r, c + 1, dp); + int p2 = Integer.MAX_VALUE; + int next2 = process2(arr, n, m, row | (1 << r), col | (1 << c), r, c + 1, dp); + if (next2 != Integer.MAX_VALUE) { + p2 = 1 + next2; + } + int ans = Math.min(p1, p2); + dp[row][col][r][c] = ans; + return ans; + } + + // 正式方法 + 贪心 + public static int setOneMinTimes3(int[][] matrix) { + int n = matrix.length; + int m = matrix[0].length; + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + int status = 0; + for (int j = 0; j < m; j++) { + if (matrix[i][j] == 1) { + status |= 1 << j; + } + } + arr[i] = status; + } + int[][][][] dp = new int[1 << n][1 << m][n][m]; + for (int a = 0; a < (1 << n); a++) { + for (int b = 0; b < (1 << m); b++) { + for (int c = 0; c < n; c++) { + for (int d = 0; d < m; d++) { + dp[a][b][c][d] = -1; + } + } + } + } + return process3(arr, n, m, 0, 0, 0, 0, dp); + } + + public static int process3(int[] arr, int n, int m, int row, int col, int r, int c, int[][][][] dp) { + if (r == n) { + for (int i = 0; i < n; i++) { + if ((row & (1 << i)) == 0 && (arr[i] | col) != (1 << m) - 1) { + return Integer.MAX_VALUE; + } + } + return 0; + } + if (c == m) { + return process3(arr, n, m, row, col, r + 1, 0, dp); + } + if (dp[row][col][r][c] != -1) { + return dp[row][col][r][c]; + } + int p1 = process3(arr, n, m, row, col, r, c + 1, dp); + int p2 = Integer.MAX_VALUE; + int next2 = process3(arr, n, m, row | (1 << r), col | (1 << c), r + 1, 0, dp); + if (next2 != Integer.MAX_VALUE) { + p2 = 1 + next2; + } + int ans = Math.min(p1, p2); + dp[row][col][r][c] = ans; + return ans; + } + + public static int[][] randomMatrix(int n, int m, double p0) { + int[][] ans = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + ans[i][j] = Math.random() < p0 ? 0 : 1; + } + } + return ans; + } + + public static void main(String[] args) { + int N = 3; + int testTimes = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int m = (int) (Math.random() * N) + 1; + double p0 = Math.random(); + int[][] matrix = randomMatrix(n, m, p0); + int ans1 = setOneMinTimes1(matrix); + int ans2 = setOneMinTimes2(matrix); + int ans3 = setOneMinTimes3(matrix); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + + int[][] matrix = randomMatrix(9, 9, 0.9); + long start = System.currentTimeMillis(); + setOneMinTimes2(matrix); + long end = System.currentTimeMillis(); + System.out.println("最极限的数据下的运行时间 : " + (end - start) + "毫秒"); + } + +} diff --git a/算法周更班/class_2022_09_4_week/Code03_MostSeats.java b/算法周更班/class_2022_09_4_week/Code03_MostSeats.java new file mode 100644 index 0000000..a16bbfd --- /dev/null +++ b/算法周更班/class_2022_09_4_week/Code03_MostSeats.java @@ -0,0 +1,292 @@ +package class_2022_09_4_week; + +import java.util.Arrays; + +// 来自华为 +// 给定一个二维数组map,代表一个餐厅,其中只有0、1两种值 +// map[i][j] == 0 表示(i,j)位置是空座 +// map[i][j] == 1 表示(i,j)位置坐了人 +// 根据防疫要求,任何人的上、下、左、右,四个相邻的方向都不能再坐人 +// 但是为了餐厅利用的最大化,也许还能在不违反防疫要求的情况下,继续安排人吃饭 +// 请返回还能安排的最大人数 +// 如果一开始的状况已经不合法,直接返回-1 +// 比如: +// 1 0 0 0 +// 0 0 0 1 +// 不违反防疫要求的情况下,这个餐厅最多还能安排2人,如下所示,X是新安排的人 +// 1 0 X 0 +// 0 X 0 1 +// 再比如: +// 1 0 0 0 0 1 +// 0 0 0 0 0 0 +// 0 1 0 0 0 1 +// 0 0 0 0 0 0 +// 不违反防疫要求的情况下,这个餐厅最多还能安排7人,如下所示,X是新安排的人 +// 1 0 0 X 0 1 +// 0 0 X 0 X 0 +// 0 1 0 X 0 1 +// X 0 X 0 X 0 +// 数据范围 : 1 <= 矩阵的行、列 <= 20 +public class Code03_MostSeats { + +// public static int maxPeople(int[][] matrix) { +// int n = matrix.length; +// int m = matrix[0].length; +// for (int i = 0; i < n; i++) { +// for (int j = 0; j < m; j++) { +// if (matrix[i][j] == 1) { +// if (i > 0 && matrix[i - 1][j] == 1) { +// return -1; +// } +// if (i < n - 1 && matrix[i + 1][j] == 1) { +// return -1; +// } +// if (j > 0 && matrix[i][j - 1] == 1) { +// return -1; +// } +// if (j < m - 1 && matrix[i][j + 1] == 1) { +// return -1; +// } +// } +// } +// } +// int[] arr = new int[n]; +// for (int i = 0; i < n; i++) { +// int status = 0; +// for (int j = 0; j < m; j++) { +// if (matrix[i][j] == 1) { +// status |= 1 << j; +// } +// +// arr[i] = status; +// } +// } +// return zuo(arr, n, m, 0, 0, 0); +// +// } +// +// public static int zuo(int[] arr, int n, int m, int i, int j, int status) { +// if (i == n) { +// return 0; +// } +// if (j == m) { +// return zuo(arr, n, m, i + 1, 0, status); +// } +// // 怎么判断已经无效了 +// if ((arr[i] & status) != 0) { +// return -1; +// } +// // (i,j) +// int left = status(status, j - 1, m); +// int up = status(status, j, m); +// int right = status(arr[i], j + 1, m); +// int cur = status(arr[i], j, m); +// // 当前位置不安排新人了 +// int p1 = -1; +// if (cur == 0) { // 原始的该位置无人 +// int nextStatus = status ^ (up << j); +// p1 = zuo(arr, n, m, i, j + 1, nextStatus); +// } else { // 原始的该位置有人 +// int nextStatus = (status | (1 << j)); +// p1 = zuo(arr, n, m, i, j + 1, nextStatus); +// } +// // 当前位置安排新人 +// int p2 = -1; +// if(left == 0 && up == 0 && right == 0 && cur == 0) { +// int nextStatus = (status | (1 << j)); +// int next = zuo(arr, n, m, i, j + 1, nextStatus); +// if(next != -1) { +// p2 = 1 + next; +// } +// } +// return Math.max(p1, p2); +// } + + // 为了测试,普通方法 + // 普通的状态压缩动态规划 + // 每一行用dfs的方法 + // 体系学习班,章节44 : 状态压缩的动态规划,贴瓷砖问题类似 + public static int mostSeats1(int[][] map) { + int n = map.length; + int m = map[0].length; + int[] arr = new int[n]; + for (int row = 0; row < n; row++) { + int status = 0; + for (int col = 0, i = m - 1; col < m; col++, i--) { + if (map[row][col] == 1) { + if (row > 0 && map[row - 1][col] == 1) { + return -1; + } + if (col > 0 && map[row][col - 1] == 1) { + return -1; + } + } + status |= map[row][col] << i; + } + arr[row] = status; + } + int[][] dp = new int[n][1 << m]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -2); + } + int ans = process1(arr, 0, 0, m, dp); + return ans == -1 ? 0 : ans; + } + + public static int process1(int[] arr, int row, int pre, int m, int[][] dp) { + if (row == arr.length) { + return 0; + } + if (dp[row][pre] != -2) { + return dp[row][pre]; + } + int cur = arr[row]; + int ans = 0; + if ((cur & pre) != 0) { + ans = -1; + } else { + ans = dfs(arr, row, m - 1, pre, cur, m, dp); + } + dp[row][pre] = ans; + return ans; + } + + public static int dfs(int[] arr, int row, int col, int pre, int seats, int m, int[][] dp) { + if (col == -1) { + return process1(arr, row + 1, seats, m, dp); + } else { + int p1 = dfs(arr, row, col - 1, pre, seats, m, dp); + int p2 = -1; + if ((pre & (1 << col)) == 0 && (seats & (1 << col)) == 0 + && (col == m - 1 || (seats & (1 << (col + 1))) == 0) + && (col == 0 || (seats & (1 << (col - 1))) == 0)) { + int next2 = dfs(arr, row, col - 1, pre, seats | (1 << col), m, dp); + if (next2 != -1) { + p2 = 1 + next2; + } + } + return Math.max(p1, p2); + } + } + + // 正式方法 + // 轮廓线dp + public static int mostSeats2(int[][] map) { + int n = map.length; + int m = map[0].length; + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + int status = 0; + for (int j = 0; j < m; j++) { + if (map[i][j] == 1) { + if (i > 0 && map[i - 1][j] == 1) { + return -1; + } + if (j > 0 && map[i][j - 1] == 1) { + return -1; + } + } + status |= map[i][j] << j; + } + arr[i] = status; + } + int s = 1 << m; + int[][][] dp = new int[n][m][s]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + for (int k = 0; k < s; k++) { + dp[i][j][k] = -2; + } + } + } + int ans = process2(arr, n, m, 0, 0, 0, dp); + return ans == -1 ? 0 : ans; + } + + // 20 * 20 * 2^20 -> 4 * 10^8 + public static int process2(int[] arr, int n, int m, int i, int j, int status, int[][][] dp) { + if (j == m) { + return process2(arr, n, m, i + 1, 0, status, dp); + } + if (i == n) { + return 0; + } + if (dp[i][j][status] != -2) { + return dp[i][j][status]; + } + int left = status(status, j - 1, m); + int up = status(status, j, m); + int cur = status(arr[i], j, m); + int right = status(arr[i], j + 1, m); + if (up == 1 && cur == 1) { + return -1; + } + int p1 = -1; + if (cur == 1) { + p1 = process2(arr, n, m, i, j + 1, status | (1 << j), dp); + } else { + p1 = process2(arr, n, m, i, j + 1, (status | (1 << j)) ^ (1 << j), dp); + } + int p2 = -1; + if (left == 0 && up == 0 && cur == 0 && right == 0) { + int next2 = process2(arr, n, m, i, j + 1, status | (1 << j), dp); + if (next2 != -1) { + p2 = 1 + next2; + } + } + int ans = Math.max(p1, p2); + dp[i][j][status] = ans; + return ans; + } + + public static int status(int status, int i, int m) { + return (i < 0 || i == m || (status & (1 << i)) == 0) ? 0 : 1; + } + + public static int[][] randomMatrix(int n, int m, double oneP) { + int[][] ans = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + ans[i][j] = Math.random() < oneP ? 1 : 0; + } + } + return ans; + } + + public static void main(String[] args) { + int N = 10; + int M = 10; + // 产生1的概率 + double oneP = 0.15; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int m = (int) (Math.random() * M) + 1; + int[][] map = randomMatrix(n, m, oneP); + int ans1 = mostSeats1(map); + int ans2 = mostSeats2(map); + if (ans1 != ans2) { + for (int[] arr : map) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + } + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + + int n = 20; + int[][] map = new int[n][n]; + System.out.println("最大规模 : " + n + " * " + n); + long start = System.currentTimeMillis(); + mostSeats2(map); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + } + +} diff --git a/算法周更班/class_2022_09_4_week/Code04_PaintingGridWithThreeDifferentColors.java b/算法周更班/class_2022_09_4_week/Code04_PaintingGridWithThreeDifferentColors.java new file mode 100644 index 0000000..e636bb7 --- /dev/null +++ b/算法周更班/class_2022_09_4_week/Code04_PaintingGridWithThreeDifferentColors.java @@ -0,0 +1,83 @@ +package class_2022_09_4_week; + +// 给你两个整数 m 和 n 。构造一个 m x n 的网格,其中每个单元格最开始是白色 +// 请你用 红、绿、蓝 三种颜色为每个单元格涂色。所有单元格都需要被涂色 +// 涂色方案需要满足:不存在相邻两个单元格颜色相同的情况 +// 返回网格涂色的方法数。因为答案可能非常大 +// 返回 对 109 + 7 取余 的结果。 +// 1 <= n <= 1000 +// 1 <= m <= 5 +// 测试链接 : https://leetcode.cn/problems/painting-a-grid-with-three-different-colors/ +public class Code04_PaintingGridWithThreeDifferentColors { + +// public static int zuo(int i, int j, int s, int n, int m) { +// if (i == n) { +// return 1; +// } +// if (j == m) { +// return zuo(i + 1, 0, s, n, m); +// } +// +// // j列 +// int up = (s >> (j * 2)) & 3; +// int left = j == 0 ? 0 : ((s >> ((j - 1) * 2)) & 3); +// int ans = 0; +// if (up != 1 && left != 1) { // 当前格子就涂1这个颜色 +// int exp = statu +// ans += zuo( i, j + 1, ?, n,m); +// +// } +// if (up != 2 && left != 2) { +// +// } +// if (up != 3 && left != 3) { +// +// } +// return ans; +// } + + public static final int mod = 1000000007; + + public static int colorTheGrid(int m, int n) { + int status = 1 << (m << 1); + int[][][] dp = new int[n][m][status]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + for (int s = 0; s < status; s++) { + dp[i][j][s] = -1; + } + } + } + return process(0, 0, 0, n, m, dp); + } + + public static int process(int i, int j, int s, int n, int m, int[][][] dp) { + if (i == n) { + return 1; + } + if (j == m) { + return process(i + 1, 0, s, n, m, dp); + } + if (dp[i][j][s] != -1) { + return dp[i][j][s]; + } + int up = (s >> (j * 2)) & 3; + int left = j == 0 ? 0 : ((s >> ((j - 1) << 1)) & 3); + int ans = 0; + if (up != 1 && left != 1) { + ans += process(i, j + 1,(s ^ (up << (j * 2))) | (1 << (j * 2)), n, m, dp); + ans %= mod; + } + if (up != 2 && left != 2) { + ans += process(i, j + 1, (s ^ (up << (j << 1))) | (2 << (j << 1)), n, m, dp); + ans %= mod; + } + if (up != 3 && left != 3) { + ans += process(i, j + 1, (s ^ (up << (j << 1))) | (3 << (j << 1)), n, m, dp); + ans %= mod; + } + dp[i][j][s] = ans; + return ans; + } + +} diff --git a/算法周更班/class_2022_09_4_week/Code05_EveryQueryUsers.java b/算法周更班/class_2022_09_4_week/Code05_EveryQueryUsers.java new file mode 100644 index 0000000..133421d --- /dev/null +++ b/算法周更班/class_2022_09_4_week/Code05_EveryQueryUsers.java @@ -0,0 +1,150 @@ +package class_2022_09_4_week; + +import java.util.HashMap; +import java.util.HashSet; + +// 来自字节 +// 给定正数N,表示用户数量,用户编号从0~N-1 +// 给定正数M,表示实验数量,实验编号从0~M-1 +// 给定长度为N的二维数组A, +// A[i] = { a, b, c }表示,用户i报名参加了a号、b号、c号实验 +// 给定正数Q,表示查询的条数 +// 给定长度为Q的二维数组B, +// B[i] = { e, f }表示,第i条查询想知道e号、f号实验,一共有多少人(去重统计) +// 返回每一条查询的结果数组 +// 数据描述 : +// 1 <= N <= 10^5 +// 1 <= M <= 10^2 +// 1 <= Q <= 10^4 +// 所有查询所列出的所有实验编号数量(也就是二维数组B,行*列的规模) <= 10^5 +public class Code05_EveryQueryUsers { + + // 暴力方法 + // 为了验证 + public static int[] record1(int n, int m, int q, int[][] A, int[][] B) { + HashMap> expUsersMap = new HashMap<>(); + for (int i = 0; i < m; i++) { + expUsersMap.put(i, new HashSet<>()); + } + for (int i = 0; i < n; i++) { + for (int exp : A[i]) { + expUsersMap.get(exp).add(i); + } + } + int[] ans = new int[q]; + HashSet help = new HashSet<>(); + for (int i = 0; i < q; i++) { + help.clear(); + for (int exp : B[i]) { + for (int user : expUsersMap.get(exp)) { + help.add(user); + } + } + ans[i] = help.size(); + } + return ans; + } + + // 正式方法 + public static int[] record2(int n, int m, int q, int[][] A, int[][] B) { + // n 一共有多少人 + // 任何一个实验,需要几个整数,能表示所有人谁出现谁没出现? + int parts = (n + 31) / 32; + // m 0 ~ m -1 + // [i] [.........] + int[][] bitMap = new int[m][parts]; + for (int i = 0; i < n; i++) { + // i 人的编号 : a b c + for (int exp : A[i]) { + bitMap[exp][i / 32] |= 1 << (i % 32); + } + } + int[] ans = new int[q]; + for (int i = 0; i < q; i++) { + // i号查询 : a、c、e,一共有多少去重的人 + // a[0] | c[0] | e[0] -> 几个1 + // a[1] | c[1] | e[1] -> 几个1 + int all = 0; + for (int j = 0; j < parts; j++) { + int status = 0; + for (int exp : B[i]) { + status |= bitMap[exp][j]; + } + all += countOnes(status); + } + ans[i] = all; + } + return ans; + } + + // 大厂刷题班,32节,leetcode专题 : https://leetcode.com/problems/number-of-1-bits/ + public static int countOnes(int n) { + n = (n & 0x55555555) + ((n >>> 1) & 0x55555555); + n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); + n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f); + n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff); + n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff); + return n; + } + + // 为了测试 + public static int[][] randomMatrix(int n, int m, int v) { + int[][] ans = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + ans[i][j] = (int) (Math.random() * v); + } + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 100; + int M = 20; + int Q = 50; + 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() * M) + 1; + int[][] A = randomMatrix(n, (int) (Math.random() * m) + 1, m); + int q = (int) (Math.random() * Q) + 1; + int[][] B = randomMatrix(q, (int) (Math.random() * m) + 1, m); + int[] ans1 = record1(n, m, q, A, B); + int[] ans2 = record2(n, m, q, A, B); + boolean pass = true; + for (int j = 0; j < q; j++) { + if (ans1[j] != ans2[j]) { + pass = false; + break; + } + } + if (!pass) { + System.out.println("出错了!"); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + int n = 100000; + int m = 100; + int[][] A = randomMatrix(n, m, m); + int q = 10000; + int c = 10; + int[][] B = randomMatrix(q, c, m); + System.out.println("用户数量 : " + n); + System.out.println("实验数量 : " + m); + System.out.println("用户参加的实验数量总和 : " + n * m); + System.out.println("查询条数 : " + q); + System.out.println("每条查询的实验数量 : " + c); + System.out.println("所有查询所列出的所有实验编号数量 : " + q * c); + long start = System.currentTimeMillis(); + record2(n, m, q, A, B); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + } + +} diff --git a/算法课堂笔记/算法和数据结构知识梳理.xmind b/算法课堂笔记/算法和数据结构知识梳理.xmind new file mode 100644 index 0000000..ea80fcc Binary files /dev/null and b/算法课堂笔记/算法和数据结构知识梳理.xmind differ diff --git a/算法课堂笔记/算法和数据结构课程全梳理.xmind b/算法课堂笔记/算法和数据结构课程全梳理.xmind deleted file mode 100644 index b9d32c2..0000000 Binary files a/算法课堂笔记/算法和数据结构课程全梳理.xmind and /dev/null differ diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 90f553a..38f0ea8 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -1985,6 +1985,85 @@ https://www.mashibing.com/question/detail/37485 +第042节 2022年9月第4周流行算法题目解析 + +来自学员问题 +智能机器人要坐专用电梯把货物送到指定地点 +整栋楼只有一部电梯,并且由于容量限制智能机器人只能放下一件货物 +给定K个货物,每个货物都有所在楼层(from)和目的楼层(to) +假设电梯速度恒定为1,相邻两个楼层之间的距离为1 +例如电梯从10层去往19层的时间为9 +机器人装卸货物的时间极快不计入 +电梯初始地点为第1层,机器人初始地点也是第1层 +并且在运送完所有货物之后,机器人和电梯都要回到1层 +返回智能机器人用电梯将每个物品都送去目标楼层的最快时间 +注意:如果智能机器人选了一件物品,则必须把这个物品送完,不能中途丢下 +输入描述: +正数k,表示货物数量,1 <= k <= 16 +from、to数组,长度都是k,1 <= from[i]、to[i] <= 10000 +from[i]表示i号货物所在的楼层 +to[i]表示i号货物要去往的楼层 +返回最快的时间 + +来自华为 +一个n*n的二维数组中,只有0和1两种值 +当你决定在某个位置操作一次 +那么该位置的行和列整体都会变成1,不管之前是什么状态 +返回让所有值全变成1,最少的操作次数 +1 < n < 10,没错!原题就是说n < 10, 不会到10!最多到9! + +来自华为 +给定一个二维数组map,代表一个餐厅,其中只有0、1两种值 +map[i][j] == 0 表示(i,j)位置是空座 +map[i][j] == 1 表示(i,j)位置坐了人 +根据防疫要求,任何人的上、下、左、右,四个相邻的方向都不能再坐人 +但是为了餐厅利用的最大化,也许还能在不违反防疫要求的情况下,继续安排人吃饭 +请返回还能安排的最大人数 +如果一开始的状况已经不合法,直接返回-1 +比如: +1 0 0 0 +0 0 0 1 +不违反防疫要求的情况下,这个餐厅最多还能安排2人,如下所示,X是新安排的人 +1 0 X 0 +0 X 0 1 +再比如: +1 0 0 0 0 1 +0 0 0 0 0 0 +0 1 0 0 0 1 +0 0 0 0 0 0 +不违反防疫要求的情况下,这个餐厅最多还能安排7人,如下所示,X是新安排的人 +1 0 0 X 0 1 +0 0 X 0 X 0 +0 1 0 X 0 1 +X 0 X 0 X 0 +数据范围 : 1 <= 矩阵的行、列 <= 20 + +给你两个整数 m 和 n 。构造一个 m x n 的网格,其中每个单元格最开始是白色 +请你用 红、绿、蓝 三种颜色为每个单元格涂色。所有单元格都需要被涂色 +涂色方案需要满足:不存在相邻两个单元格颜色相同的情况 +返回网格涂色的方法数。因为答案可能非常大 +返回 对 109 + 7 取余 的结果。 +1 <= n <= 1000 +1 <= m <= 5 +测试链接 : https://leetcode.cn/problems/painting-a-grid-with-three-different-colors/ + +来自字节 +给定正数N,表示用户数量,用户编号从0~N-1 +给定正数M,表示实验数量,实验编号从0~M-1 +给定长度为N的二维数组A, +A[i] = { a, b, c }表示,用户i报名参加了a号、b号、c号实验 +给定正数Q,表示查询的条数 +给定长度为Q的二维数组B, +B[i] = { e, f }表示,第i条查询想知道e号、f号实验,一共有多少人(去重统计) +返回每一条查询的结果数组 +数据描述 : +1 <= N <= 10^5 +1 <= M <= 10^2 +1 <= Q <= 10^4 +所有查询所列出的所有实验编号数量(也就是二维数组B,行*列的规模) <= 10^5 + + +