From 598edb3a3a81df53b083b88cfef00b358f91f73f Mon Sep 17 00:00:00 2001 From: Leo <582717189@qq.com> Date: Mon, 11 Jan 2021 16:48:45 +0800 Subject: [PATCH] 20 --- src/leo/class20/Coffee.java | 214 +++++++++++++++++++++ src/leo/class20/HorseJump.java | 91 +++++++++ src/leo/class20/PalindromeSubsequence.java | 113 +++++++++++ 3 files changed, 418 insertions(+) create mode 100644 src/leo/class20/Coffee.java create mode 100644 src/leo/class20/HorseJump.java create mode 100644 src/leo/class20/PalindromeSubsequence.java diff --git a/src/leo/class20/Coffee.java b/src/leo/class20/Coffee.java new file mode 100644 index 0000000..97ba9f3 --- /dev/null +++ b/src/leo/class20/Coffee.java @@ -0,0 +1,214 @@ +package leo.class20; + +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * @author Leo + * @ClassName Coffee + * @DATE 2021/1/11 3:00 下午 + * @Description + * 业务限制模型,可变参数不能直观的得到变换范围,需要把可变参数估出来, + * 样本对应模式,两个参数就是下标,明确知道两个参数的变化范围,只讨论当前的结尾如何组织可能性 + * 范围尝试模型,LR两个参数可变参数,左下半区没用,先求对角线或上一个对角线,,只用上半区,特别在意开头和结尾. + * 从左往右模型,明确知道可变参数的最大值, + * + * 题目 + * 数组arr代表每一个咖啡机冲一杯咖啡的时间,每个咖啡机只能串行的制造咖啡。 + * 现在有n个人需要喝咖啡,只能用咖啡机来制造咖啡。 + * 认为每个人喝咖啡的时间非常短,冲好的时间即是喝完的时间。 + * 每个人喝完之后咖啡杯可以选择洗或者自然挥发干净,只有一台洗咖啡杯的机器,只能串行的洗咖啡杯。 + * 洗杯子的机器洗完一个杯子时间为a,任何一个杯子自然挥发干净的时间为b。 + * 四个参数:arr, n, a, b + * 假设时间点从0开始,返回所有人喝完咖啡并洗完咖啡杯的全部过程结束后,至少来到什么时间点。 + */ +public class Coffee { + + /** + * 暴力递归到动态规划 + * 分析可变参数以及依赖关系 + * 如果分析不出依赖参数,使用记忆化搜索,傻缓存 + * + * 动态规划模型: + * 业务限制模型,可变参数不能直观的得到变换范围,需要把可变参数估出来,可变参数用业务最差参数估, + * 样本对应模式,两个参数就是下标,明确知道两个参数的变化范围,只讨论当前的结尾如何组织可能性 + * 范围尝试模型,LR两个范围参数可变参数,左下半区没用,先求对角线或上一个对角线,L>R的时候不需要填,只用上半区,特别在意开头和结尾. + * 从左往右模型,明确知道可变参数的最大值 + */ + + + static class Machine { + //提供服务的时间点 + int timePoint; + //工作一次的时间 + int workTime; + public Machine(int t,int w) { + timePoint = t; + workTime = w; + } + } + + static class MachineComparator implements Comparator { + + @Override + public int compare(Machine o1, Machine o2) { + return (o1.timePoint + o1.workTime) - (o2.timePoint + o2.workTime); + } + } + static class Recursion { + + /** + * 功能描述 : 每个人喝完之后咖啡杯可以选择洗或者自然挥发干净,只有一台洗咖啡杯的机器,只能串行的洗咖啡杯。 + * 假设时间点从0开始,返回所有人喝完咖啡并洗完咖啡杯的全部过程结束后,至少来到什么时间点。 + * @author Leo + * @date 2021/1/11 3:15 下午 + * @param arr 数组arr代表每一个咖啡机冲一杯咖啡的时间,每个咖啡机只能串行的制造咖啡。 + * @param n 现在有n个人需要喝咖啡,只能用咖啡机来制造咖啡。 + * @param a 洗杯子的机器洗完一个杯子时间为a, + * @param b 任何一个杯子自然挥发干净的时间为b。 + * @return int + */ + public static int minTime(int[] arr, int n, int a, int b) { + PriorityQueue heap = new PriorityQueue<>(new MachineComparator()); + for (int i = 0; i < arr.length; i++) { + heap.offer(new Machine(0, arr[i])); + } + //所有杯子可以开始洗的时间 + int[] drinks = new int[n]; + for (int i = 0; i < n; i++) { + Machine cur = heap.poll(); + cur.timePoint += cur.workTime; + drinks[i] = cur.timePoint; + heap.offer(cur); + } + return process(drinks, a, b, 0, 0); + } + + /** + * 功能描述 : + * @author Leo + * @date 2021/1/11 3:21 下午 + * @param drinks 所有杯子可以开始洗的时间 + * @param wash 洗杯子的时间 + * @param air 不洗杯子挥发干净的时间 + * @param i 第几个杯子 + * @param free 洗杯子机器什么时间可用. + * @throw + * @return int + */ + private static int process(int[] drinks, int wash, int air, int i, int free) { + if (i == drinks.length) { + return 0; + } + //洗 + int washClean = Math.max(drinks[i], free) + wash; + //洗的方式剩余干净的时间 + int restWashClean = process(drinks, wash, air, i + 1, washClean); + int p1 = Math.max(washClean, restWashClean); + //挥发 + int airClean = drinks[i] + air; + //挥发的方式剩余干净的时间 + int restAirClean = process(drinks, wash, air, i + 1, free); + int p2 = Math.max(airClean, restAirClean); + return Math.min(p1, p2); + } + + + } + + static class Dp { + public static int minTime(int[] arr, int n, int a, int b) { + PriorityQueue heap = new PriorityQueue<>(new MachineComparator()); + for (int i = 0; i < arr.length; i++) { + heap.offer(new Machine(0, arr[i])); + } + int[] drinks = new int[n]; + for (int i = 0; i < n; i++) { + Machine cur = heap.poll(); + cur.timePoint += cur.workTime; + drinks[i] = cur.timePoint; + heap.offer(cur); + } + + return process(drinks, a, b); + } + + private static int process(int[] drinks, int wash, int air) { + int n = drinks.length; + //求每个杯子洗完的最大时间 + int maxFree = 0; + for (int i = 0; i < n; i++) { + maxFree = Math.max(maxFree, drinks[i]) + wash; + } + int[][] dp = new int[n + 1][maxFree + 1]; + //上面依赖下面,从地下开始填 + for (int i = n - 1; i >= 0; i--) { + //从左往右填 + for (int free = 0; free < maxFree; free++) { + //洗 + int washClean = Math.max(drinks[i], free) + wash; + //没有这个位置,防越界 + if (washClean > maxFree) { + continue; + } + //洗的方式剩余干净的时间 + int restWashClean = dp[i + 1][washClean]; + int p1 = Math.max(washClean, restWashClean); + //挥发 + int airClean = drinks[i] + air; + //挥发的方式剩余干净的时间 + int restAirClean = dp[i + 1][free]; + int p2 = Math.max(airClean, restAirClean); + dp[i][free] = Math.min(p1, p2); + } + } + return dp[0][0]; + } + + } + + + public static void main(String[] args) { + int len = 10; + int max = 10; + int testTime = 10; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(len, max); + int n = (int) (Math.random() * 7) + 1; + int a = (int) (Math.random() * 7) + 1; + int b = (int) (Math.random() * 10) + 1; + int recursion = Recursion.minTime(arr, n, a, b); + int dp = Dp.minTime(arr, n, a, b); + if (recursion != dp ) { + printArray(arr); + System.out.println("n : " + n); + System.out.println("a : " + a); + System.out.println("b : " + b); + System.out.println(recursion + " , " + dp); + System.out.println("==============="); + break; + } + } + System.out.println("测试结束"); + + } + + // for test + public static int[] randomArray(int len, int max) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * max) + 1; + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + System.out.print("arr : "); + for (int j = 0; j < arr.length; j++) { + System.out.print(arr[j] + ", "); + } + System.out.println(); + } +} diff --git a/src/leo/class20/HorseJump.java b/src/leo/class20/HorseJump.java new file mode 100644 index 0000000..f64e502 --- /dev/null +++ b/src/leo/class20/HorseJump.java @@ -0,0 +1,91 @@ +package leo.class20; + +/** + * @author Leo + * @ClassName HorseJump + * @DATE 2021/1/11 1:34 下午 + * @Description + * 样本对应模型 + * + * 请同学们自行搜索或者想象一个象棋的棋盘, + * 然后把整个棋盘放入第一象限,棋盘的最左下角是(0,0)位置 + * 那么整个棋盘就是横坐标上9条线、纵坐标上10条线的区域 + * 给你三个 参数 x,y,k + * 返回“马”从(0,0)位置出发,必须走k步 + * 最后落在(x,y)上的方法数有多少种? + */ +public class HorseJump { + + /** + * O(8^k) + */ + static class Recursion { + + public static int jump(int a, int b, int k) { + return process(0, 0, k, a, b); + } + + private static int process(int x, int y, int rest, int a, int b) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + if (rest == 0) { + return x == a && y == b ? 1 : 0; + } + int ways = process(x - 1, y - 2, rest - 1, a, b); + ways += process(x - 1, y + 2, rest - 1, a, b); + ways += process(x + 1, y - 2, rest - 1, a, b); + ways += process(x + 1, y + 2, rest - 1, a, b); + + ways += process(x - 2, y + 1, rest - 1, a, b); + ways += process(x - 2, y - 1, rest - 1, a, b); + ways += process(x + 2, y + 1, rest - 1, a, b); + ways += process(x + 2, y - 1, rest - 1, a, b); + return ways; + } + } + + /** + * O(k) + */ + static class Dp { + public static int jump(int a, int b, int k) { + int[][][] dp = new int[10][9][k + 1]; + dp[a][b][0] = 1; + for (int rest = 1; rest <= k; rest++) { + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 9; y++) { + int ways = pick(dp, x - 1, y - 2, rest - 1); + ways += pick(dp, x - 1, y + 2, rest - 1); + ways += pick(dp, x + 1, y - 2, rest - 1); + ways += pick(dp, x + 1, y + 2, rest - 1); + + ways += pick(dp, x - 2, y + 1, rest - 1); + ways += pick(dp, x - 2, y - 1, rest - 1); + ways += pick(dp, x + 2, y + 1, rest - 1); + ways += pick(dp, x + 2, y - 1, rest - 1); + dp[x][y][rest] = ways; + } + } + } + return dp[0][0][k]; + } + + private static int pick(int[][][] dp, int x, int y, int rest) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + return dp[x][y][rest]; + } + } + + + + public static void main(String[] args){ + int x = 7; + int y = 7; + int step = 10; + System.out.println(Recursion.jump(x, y, step)); + System.out.println(Dp.jump(x, y, step)); + } +} diff --git a/src/leo/class20/PalindromeSubsequence.java b/src/leo/class20/PalindromeSubsequence.java new file mode 100644 index 0000000..c9b784f --- /dev/null +++ b/src/leo/class20/PalindromeSubsequence.java @@ -0,0 +1,113 @@ +package leo.class20; + +import java.util.function.IntPredicate; + +/** + * @author Leo + * @ClassName PalindromeSubsequence + * @DATE 2021/1/11 10:12 上午 + * @Description 最长回文子序列 + * https://leetcode-cn.com/problems/longest-palindromic-subsequence/ + * 子序列可以不连续 + * 子串必须连续 + * 范围尝试模型特别在意开头和结尾. + */ +public class PalindromeSubsequence { + + /** + * 一个字符串的逆序最长子序列,就是这个字符串的最长回文子序列 + */ + static class Reverse { + + } + + static class Recursion { + public static int longestPalindromeSubseq(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] chars = s.toCharArray(); + return f(chars, 0, chars.length - 1); + } + + /** + * 功能描述 : 求一个字符串从l到r范围中的最长回文子序列长度 + * @author Leo + * @date 2021/1/11 10:21 上午 + * @param str + * @param l + * @param r + * @return int + */ + public static int f(char[] str, int l, int r) { + if (l == r) { + return 1; + } + if (l == r - 1) { + return str[l] == str[r] ? 2 : 1; + } + int p1 = f(str, l + 1, r); + int p2 = f(str, l, r - 1); + int p3 = f(str, l + 1, r - 1); + int p4 = str[l] == str[r] ? (2 + f(str, l + 1, r - 1)) : 0; + return Math.max(Math.max(p1, p2), Math.max(p3, p4)); + } + } + + static class Dp { + public static int longestPalindromeSubseq(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int n = str.length; + int[][] dp = new int[n][n]; + dp[n - 1][n - 1] = 1; + for (int i = 0; i < n - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1; + } + //从底网上填 + for (int l = n - 3; l >= 0; l--) { + for (int r = l + 2; r < n; r++) { + int p1 = dp[l + 1][r]; + int p2 = dp[l][r - 1]; + int p3 = dp[l + 1][r - 1]; + int p4 = str[l] == str[r] ? (2 + dp[l + 1][r - 1]) : 0; + dp[l][r] = Math.max(Math.max(p1, p2), Math.max(p3, p4)); + } + } + return dp[0][n - 1]; + } + + public static int longestPalindromeSubseqMajorization(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int n = str.length; + int[][] dp = new int[n][n]; + dp[n - 1][n - 1] = 1; + for (int i = 0; i < n - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1; + } + //从底网上填 + for (int l = n - 3; l >= 0; l--) { + for (int r = l + 2; r < n; r++) { + dp[l][r] = Math.max(dp[l][r - 1], dp[l + 1][r]); + if (str[l] == str[r]) { + dp[l][r] = Math.max(dp[l][r], 2 + dp[l + 1][r - 1]); + } + } + } + return dp[0][n - 1]; + } + } + + + public static void main(String[] args){ + int a = Dp.longestPalindromeSubseq("a"); + System.out.println(a); + } +}