diff --git a/src/leo/class21/BobDie.java b/src/leo/class21/BobDie.java new file mode 100644 index 0000000..ef53f31 --- /dev/null +++ b/src/leo/class21/BobDie.java @@ -0,0 +1,76 @@ +package leo.class21; + +/** + * @author Leo + * @ClassName BobDie + * @DATE 2021/1/14 2:39 下午 + * @Description + * + * 给定5个参数,N,M,row,col,k + * 表示在N*M的区域上,醉汉Bob初始在(row,col)位置 + * Bob一共要迈出k步,且每步都会等概率向上下左右四个方向走一个单位 + * 任何时候Bob只要离开N*M的区域,就直接死亡 + * 返回k步之后,Bob还在N*M的区域的概率 + */ +public class BobDie { + static class Recursion { + public static double livePosibility(int n, int m, int row, int col, int k) { + if (row < 0 || col < 0 || row >= n || col >= m) { + return 0; + } + return (double)process(n, m, row, col, k)/Math.pow(4,k); + } + + private static long process(int n, int m, int row, int col, int rest) { + if (row < 0 || col < 0 || row >= n || col >= m) { + return 0; + } + if (rest == 0) { + return 1; + } + long p1 = process(n, m, row, col + 1, rest - 1); + long p2 = process(n, m, row , col - 1, rest - 1); + long p3 = process(n, m, row + 1, col, rest - 1); + long p4 = process(n, m, row - 1, col, rest - 1); + return p1 + p2 + p3 + p4; + } + } + static class Dp { + public static double livePosibility(int n, int m, int row, int col, int k) { + if (row < 0 || col < 0 || row >= n || col >= m) { + return 0; + } + long[][][] dp = new long[n][m][k + 1]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + dp[i][j][0] = 1; + } + } + for (int rest = 1; rest <= k; rest++) { + for (int r = 0; r < n; r++) { + for (int c = 0; c < m; c++) { + dp[r][c][rest] = pick(dp,r,c+1,n,m,rest-1); + dp[r][c][rest] += pick(dp,r,c-1,n,m,rest-1); + dp[r][c][rest] += pick(dp,r+1,c,n,m,rest-1); + dp[r][c][rest] += pick(dp,r-1,c,n,m,rest-1); + } + } + } + return (double) dp[row][col][k]/Math.pow(4,k); + } + + private static long pick(long[][][] dp, int r, int c, int n, int m, int rest) { + + if (r < 0 || c < 0 || r >= n || c >= m) { + return 0; + } + return dp[r][c][rest]; + } + } + + public static void main(String[] args) { + System.out.println(Recursion.livePosibility(6, 6, 3, 3, 5)); + System.out.println(Dp.livePosibility(6, 6, 3, 3, 5)); + } + +} diff --git a/src/leo/class21/CoinsWayEveryPaperDifferent.java b/src/leo/class21/CoinsWayEveryPaperDifferent.java new file mode 100644 index 0000000..0f6ffdf --- /dev/null +++ b/src/leo/class21/CoinsWayEveryPaperDifferent.java @@ -0,0 +1,131 @@ +package leo.class21; + +import class21.Code02_CoinsWayEveryPaperDifferent; + +/** + * @author Leo + * @ClassName CoinsWayEveryPaperDifferent + * @DATE 2021/1/13 4:46 下午 + * @Description + * arr是货币数组,其中的值都是正数。再给定一个正数aim。 + * 每个值都认为是一张货币, + * 即便是值相同的货币也认为每一张都是不同的,只有一张 + * 返回组成aim的方法数 + */ +public class CoinsWayEveryPaperDifferent { + + static class Recursion { + public static int coinWays(int[] arr, int aim) { + return process(arr, aim, 0); + } + + private static int process(int[] arr, int rest, int i) { + if (rest < 0) { + return 0; + } + if (i == arr.length) { + return rest == 0 ? 1 : 0; + }else{ + return process(arr, rest, i + 1) + process(arr, rest - arr[i], i + 1); + } + } + + } + + static class Recursion1 { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + return process(arr, aim, 0); + } + + private static int process(int[] arr, int rest, int i) { + if (rest < 0) { + return 0; + } + if (i == arr.length) { + return rest == 0 ? 1 : 0; + } + return process(arr, rest, i + 1) + process(arr, rest - arr[i], i + 1); + + } + + } + + static class dp1 { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + int n = arr.length; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n - 1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + dp[i][rest] = dp[i + 1][rest] + (rest - arr[i] >= 0 ? dp[i + 1][rest - arr[i]] : 0); + } + } + + return dp[0][aim]; + } + } + + static class Dp { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + int n = arr.length; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n - 1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + dp[i][rest] = dp[i + 1][rest] + (rest - arr[i] >= 0 ? dp[i + 1][rest - arr[i]] : 0); + } + } + return dp[0][aim]; + } + } + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = Recursion1.coinWays(arr, aim); + int ans2 = dp1.coinWays(arr, aim); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/src/leo/class21/CoinsWayNoLimit.java b/src/leo/class21/CoinsWayNoLimit.java new file mode 100644 index 0000000..440747e --- /dev/null +++ b/src/leo/class21/CoinsWayNoLimit.java @@ -0,0 +1,163 @@ +package leo.class21; + +/** + * @author Leo + * @ClassName CoinsWayNoLimit + * @DATE 2021/1/13 5:21 下午 + * @Description + * + * arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。 + * 每个值都认为是一种面值,且认为张数是无限的。 + * 返回组成aim的方法数 + */ +public class CoinsWayNoLimit { + + static class Recursion { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim == 0) { + return 0; + } + return process(arr, 0, aim); + } + + private static int process(int[] arr, int i, int rest) { + if (i == arr.length) { + return rest == 0 ? 1 : 0; + } + int ans = 0; + for (int z = 0; z * arr[i] <= rest; z++) { + ans += process(arr, i + 1, rest - z * arr[i]); + } + return ans; + } + } + + static class Recursion1 { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + return process(arr, aim, 0); + } + + private static int process(int[] arr, int rest, int i) { + if (i == arr.length) { + return rest == 0 ? 1 : 0; + } + int ans = 0; + for (int z = 0; z * arr[i] <= rest; z++) { + ans += process(arr, rest - z*arr[i], i + 1); + } + return ans; + } + } + + static class Dp1 { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + int n = arr.length; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n-1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + int ans = 0; + for (int z = 0; z * arr[i] <= rest; z++) { + ans += dp[i + 1][rest - z * arr[i]]; + } + dp[i][rest] = ans; + } + } + return dp[0][aim]; + } + } + static class Dp { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + int n = arr.length; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n - 1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + int ans = 0; + for (int z = 0; z * arr[i] <= rest; z++) { + ans += dp[i + 1][rest - z * arr[i]]; + } + dp[i][rest] = ans; + } + } + + return dp[0][aim]; + } + } + + /** + * 观察严格以来表结构,简化枚举过程 + */ + static class OptimizationDp { + public static int coinWays(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + int n = arr.length; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n - 1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + dp[i][rest] = dp[i + 1][rest]; + if (rest - arr[i] >= 0) { + dp[i][rest] += dp[i][rest - arr[i]]; + } + } + } + + return dp[0][aim]; + } + } + + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = OptimizationDp.coinWays(arr, aim); + int ans2 = Dp.coinWays(arr, aim); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/src/leo/class21/CoinsWaySameValueSamePapper.java b/src/leo/class21/CoinsWaySameValueSamePapper.java new file mode 100644 index 0000000..5f94594 --- /dev/null +++ b/src/leo/class21/CoinsWaySameValueSamePapper.java @@ -0,0 +1,223 @@ +package leo.class21; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Leo + * @ClassName CoinsWaySameValueSamePapper + * @DATE 2021/1/14 11:07 上午 + * @Description + * + * arr是货币数组,其中的值都是正数。再给定一个正数aim。 + * 每个值都认为是一张货币, + * 认为值相同的货币没有任何不同, + * 返回组成aim的方法数 + */ +public class CoinsWaySameValueSamePapper { + + static class Recursion{ + static class Info { + int[] count; + int[] coins; + public Info(int[] coins,int[] count){ + this.coins = coins; + this.count = count; + } + } + private static Info getInfo(int[] arr) { + Map map = new HashMap<>(); + for (int i = 0; i < arr.length; i++) { + if (map.containsKey(arr[i])) { + map.put(arr[i], map.get(arr[i]) + 1); + }else{ + map.put(arr[i], 1); + } + } + int[] count = new int[map.size()]; + int[] coins = new int[map.size()]; + int i = 0; + for (Map.Entry entry : map.entrySet()) { + coins[i] = entry.getKey(); + count[i++] = entry.getValue(); + } + return new Info(coins, count); + } + + public static int coinsWay(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + Info info = getInfo(arr); + return process(info.coins, info.count, aim, 0); + } + + private static int process(int[] coins, int[] count, int rest, int i) { + if (rest < 0) { + return 0; + } + if (i == coins.length) { + return rest == 0 ? 1 : 0; + } + int ans = 0; + for (int z = 0; coins[i] * z <= rest && z <= count[i]; z++) { + ans += process(coins, count, rest - z * coins[i], i + 1); + } + return ans; + } + } + + static class Dp { + static class Info { + int[] coins; + int[] count; + + public Info(int[] coins, int[] count) { + this.coins = coins; + this.count = count; + } + } + private static Info getInfo(int[] arr) { + Map map = new HashMap<>(); + for (int i = 0; i < arr.length; i++) { + if (map.containsKey(arr[i])) { + map.put(arr[i], map.get(arr[i]) + 1); + }else{ + map.put(arr[i], 1); + } + } + int[] coins = new int[map.size()]; + int[] count = new int[map.size()]; + int i = 0; + for (Map.Entry entry : map.entrySet()) { + coins[i] = entry.getKey(); + count[i++] = entry.getValue(); + } + return new Info(coins, count); + } + + public static int coinsWay(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + Info info = getInfo(arr); + int n = info.coins.length; + int[] coins = info.coins; + int[] count = info.count; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n - 1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + int ans = 0; + for (int z = 0; coins[i] * z <= rest && z <= count[i]; z++) { + ans += dp[i + 1][rest - z * coins[i]]; + } + dp[i][rest] = ans; + } + } + return dp[0][aim]; + } + + } + + /** + * 观察严格以来表结构,简化枚举过程 + */ + static class OptimizationDp { + static class Info{ + int[] coins; + int[] count; + + public Info(int[] coins, int[] count) { + this.coins = coins; + this.count = count; + } + } + + private static Info getInfo(int[] arr) { + Map map = new HashMap<>(); + for (int i = 0; i < arr.length; i++) { + if (map.containsKey(arr[i])) { + map.put(arr[i], map.get(arr[i]) + 1); + }else{ + map.put(arr[i], 1); + } + } + int[] coins = new int[map.size()]; + int[] count = new int[map.size()]; + int i = 0; + for (Map.Entry entry : map.entrySet()) { + coins[i] = entry.getKey(); + count[i++] = entry.getValue(); + } + return new Info(coins, count); + } + + + public static int coinsWay(int[] arr, int aim) { + if (arr == null || arr.length == 0) { + return aim == 0 ? 1 : 0; + } + Info info = getInfo(arr); + int[] coins = info.coins; + int[] count = info.count; + int n = coins.length; + int[][] dp = new int[n + 1][aim + 1]; + dp[n][0] = 1; + for (int i = n - 1; i >= 0; i--) { + for (int rest = 0; rest <= aim; rest++) { + dp[i][rest] = dp[i + 1][rest]; + if (rest - coins[i] >= 0) { + dp[i][rest] += dp[i][rest - coins[i]]; + } + if (rest - coins[i] * (count[i] + 1) >= 0) { + dp[i][rest] -= dp[i + 1][rest - coins[i] * (count[i] + 1)]; + } + } + } + + return dp[0][aim]; + } + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 20; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = OptimizationDp.coinsWay(arr, aim); + int ans2 = Dp.coinsWay(arr, aim); + if (ans1 != ans2 ) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } +} diff --git a/src/leo/class21/MinPathSum.java b/src/leo/class21/MinPathSum.java new file mode 100644 index 0000000..2fccfee --- /dev/null +++ b/src/leo/class21/MinPathSum.java @@ -0,0 +1,210 @@ +package leo.class21; + +import class21.Code01_MinPathSum; + +/** + * @author Leo + * @ClassName MinPathSum + * @DATE 2021/1/13 2:15 下午 + * @Description + * 给定一个二维数组matrix,一个人必须从左上角出发,最后到达右下角 + * 沿途只可以向下或者向右走,沿途的数字都累加就是距离累加和 + * 返回最小距离累加和 + */ +public class MinPathSum { + + static class Recursion { + public static int minPathSum(int[][] arr) { + if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) { + return 0; + } + return process(arr, 0, 0); + } + + private static int process(int[][] arr, int r, int l) { + int min = arr[r][l]; + if (r == arr.length-1 && l == arr[0].length-1) { + return min; + } else if (r == arr.length-1 && l < arr[0].length-1) { + return min + process(arr, r, l + 1); + } else if (r < arr.length-1 && l == arr[0].length-1) { + return min + process(arr, r + 1, l); + }else { + min += Math.min(process(arr, r + 1, l), process(arr, r, l + 1)); + return min; + } + + } + + } + + static class Recursion1 { + public static int minPathSum(int[][] arr) { + if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) { + return 0; + } + return process(arr, arr.length - 1, arr[0].length - 1); + } + + private static int process(int[][] arr, int row, int col) { + int min = arr[row][col]; + if (row == 0 && col == 0) { + return min; + } else if (row == 0 && col > 0) { + return min + process(arr, row, col - 1); + } else if (row > 0 && col == 0) { + return min + process(arr, row - 1, col); + }else{ + return min + Math.min(process(arr, row - 1, col), process(arr, row, col - 1)); + } + + } + } + + static class Dp { + public static int minPathSum(int[][] arr) { + if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) { + return 0; + } + int r = arr.length; + int l = arr[0].length; + int[][] dp = new int[r][l]; + dp[r - 1][l - 1] = arr[r-1][l-1]; + for (int i = r - 2; i >= 0; i--) { + dp[i][l-1] = arr[i][l-1] + dp[i + 1][l-1]; + } + for (int i = l - 2; i >= 0; i--) { + dp[r-1][i] = arr[r-1][i] + dp[r-1][i + 1]; + } + for (int i = r - 2; i >= 0; i--) { + for (int j = l - 2; j >= 0; j--) { + dp[i][j] = arr[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + } + } + return dp[0][0]; + } + } + + static class Dp1 { + public static int minPathSum(int[][] arr) { + if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) { + return 0; + } + int row = arr.length; + int col = arr[0].length; + int[][] dp = new int[row][col]; + dp[0][0] = arr[0][0]; + for (int i = 1; i < row; i++) { + dp[i][0] = arr[i][0] + dp[i - 1][0]; + } + for (int i = 1; i < col; i++) { + dp[0][i] = arr[0][i] + dp[0][i - 1]; + } + for (int r = 1; r < row; r++) { + for (int c = 1; c < col; c++) { + dp[r][c] = arr[r][c] + Math.min(dp[r - 1][c], dp[r][c - 1]); + } + } + return dp[row - 1][col - 1]; + } + } + + static class OptimizationDp1 { + public static int minPathSum(int[][] arr) { + if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) { + return 0; + } + int row = arr.length; + int col = arr[0].length; + int[] dp = new int[col]; + dp[0] = arr[0][0]; + for (int i = 1; i < col; i++) { + dp[i] = arr[0][i] + dp[i - 1]; + } + for (int r = 1; r < row; r++) { + dp[0] += arr[r][0]; + for (int c = 1; c < col; c++) { + dp[c] = arr[r][c] + Math.min(dp[c - 1], dp[c]); + } + } + return dp[col - 1]; + } + } + + + static class OptimizationDp { + public static int minPathSum(int[][] arr) { + if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) { + return 0; + } + int r = arr.length; + int c = arr[0].length; + int[] dp = new int[c]; + dp[c-1] = arr[r - 1][c - 1]; + for (int i = c - 2; i >= 0; i--) { + dp[i] = arr[r - 1][i] + dp[i + 1]; + } + for (int i = r - 2; i >= 0; i--) { + dp[c - 1] += arr[i][c - 1]; + for (int j = c - 2; j >= 0; j--) { + dp[j] = arr[i][j] + Math.min(dp[j + 1], dp[j]); + } + } + + return dp[0]; + } + + + } + // for test + public static int[][] generateRandomMatrix(int rowSize, int colSize) { + if (rowSize < 0 || colSize < 0) { + return null; + } + int[][] result = new int[rowSize][colSize]; + for (int i = 0; i != result.length; i++) { + for (int j = 0; j != result[0].length; j++) { + result[i][j] = (int) (Math.random() * 10); + } + } + return result; + } + + // for test + public static void printMatrix(int[][] matrix) { + for (int i = 0; i != matrix.length; i++) { + for (int j = 0; j != matrix[0].length; j++) { + System.out.print(matrix[i][j] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int[][] m = generateRandomMatrix(4, 4); + /* int[][] m = { + {7, 2, 8, 4}, + {1, 2, 0, 8}, + {7, 5, 1, 6}, + {0, 7, 0, 7} + };*/ + System.out.println("start"); + + for (int i = 0; i < 10000; i++) { + int recursion = Recursion1.minPathSum(m); + int dp = Dp.minPathSum(m); + int dp1 = Dp1.minPathSum(m); + int opt1 = OptimizationDp1.minPathSum(m); + int opt = OptimizationDp.minPathSum(m); + if (opt1 != opt) { + System.out.println("===="); + printMatrix(m); + System.out.println(opt); + System.out.println(opt1); + break; + } + } + System.out.println("end"); + + } +} diff --git a/src/leo/remark.md b/src/leo/remark.md new file mode 100644 index 0000000..ab930cb --- /dev/null +++ b/src/leo/remark.md @@ -0,0 +1,13 @@ +## dp + +- 业务限制模型,可变参数不能直观的得到变换范围,需要把可变参数估出来, +- 样本对应模式,两个参数就是下标,明确知道两个参数的变化范围,只讨论当前的结尾如何组织可能性 +- 范围尝试模型,LR两个参数可变参数,左下半区没用,先求对角线或上一个对角线,,只用上半区,特别在意开头和结尾. +- 从左往右模型,明确知道可变参数的最大值 + +**如果一个格子没有枚举行为,只依赖有限的几个格子,记忆化搜索和严格表结构的答案(时间复杂度)同样的好** +**如果有枚举行为,需要搞出严格表结构继续优化** +## String + +- 子序列可以不连续 +- 子串必须连续