pull/3/head
Leo 6 years ago
parent 8fdf1142f0
commit 598edb3a3a

@ -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<Machine> {
@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<Machine> 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<Machine> 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();
}
}

@ -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));
}
}

@ -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);
}
}
Loading…
Cancel
Save