diff --git a/src/class22/Code01_KillMonster.java b/src/class22/Code01_KillMonster.java index 7a0756b9..77fae7b3 100644 --- a/src/class22/Code01_KillMonster.java +++ b/src/class22/Code01_KillMonster.java @@ -2,72 +2,78 @@ public class Code01_KillMonster { - public static double right1(int N, int M, int K) { + public static double right(int N, int M, int K) { if (N < 1 || M < 1 || K < 1) { return 0; } long all = (long) Math.pow(M + 1, K); - long kill = process1(N, M, K); + long kill = process(K, M, N); return (double) ((double) kill / (double) all); } - public static long process1(int N, int M, int K) { - if (K == 0) { - return N <= 0 ? 1 : 0; + // 怪兽还剩hp点血 + // 每次的伤害在[0~M]范围上 + // 还有times次可以砍 + // 返回砍死的情况数! + public static long process(int times, int M, int hp) { + if (times == 0) { + return hp <= 0 ? 1 : 0; + } + if (hp <= 0) { + return (long) Math.pow(M + 1, times); } long ways = 0; for (int i = 0; i <= M; i++) { - ways += process1(N - i, M, K - 1); + ways += process(times - 1, M, hp - i); } return ways; } - public static double right2(int N, int M, int K) { + public static double dp1(int N, int M, int K) { if (N < 1 || M < 1 || K < 1) { return 0; } long all = (long) Math.pow(M + 1, K); - long kill = process2(N, M, K); - return (double) ((double) kill / (double) all); - } - - public static long process2(int N, int M, int K) { - if (K == 0) { - return N <= 0 ? 1 : 0; - } - if (N <= 0) { - return (long) (Math.pow(M + 1, K)); + long[][] dp = new long[K + 1][N + 1]; + dp[0][0] = 1; + for (int times = 1; times <= K; times++) { + dp[times][0] = (long) Math.pow(M + 1, times); + for (int hp = 1; hp <= N; hp++) { + long ways = 0; + for (int i = 0; i <= M; i++) { + if (hp - i >= 0) { + ways += dp[times - 1][hp - i]; + } else { + ways += (long) Math.pow(M + 1, times - 1); + } + } + dp[times][hp] = ways; + } } - return process2(N, M, K - 1) + process2(N - 1, M, K) - process2(N - M - 1, M, K - 1); + long kill = dp[K][N]; + return (double) ((double) kill / (double) all); } - // M = 5 - // 以下为斜率优化改进枚举行为的动态规划 - // dp[8][5] = dp[8..3][4] - // dp[9][5] = dp[9..4][4] = dp[9][4] + dp[8][5] - dp[3][4] - // 可以推出 - // dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i - M - 1][j-1] - public static double dp(int N, int M, int K) { + public static double dp2(int N, int M, int K) { if (N < 1 || M < 1 || K < 1) { return 0; } - long[][] dp = new long[N + 1][K + 1]; - // 特别注意:dp[0][j]既表示原含义,也表示M+1的j次方的值 + long all = (long) Math.pow(M + 1, K); + long[][] dp = new long[K + 1][N + 1]; dp[0][0] = 1; - for (int j = 1; j <= K; j++) { - dp[0][j] = dp[0][j - 1] * (M + 1); - } - for (int j = 1; j <= K; j++) { - for (int i = 1; i <= N; i++) { - dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; - if (i - M - 1 < 0) { - dp[i][j] -= dp[0][j - 1]; + for (int times = 1; times <= K; times++) { + dp[times][0] = (long) Math.pow(M + 1, times); + for (int hp = 1; hp <= N; hp++) { + dp[times][hp] = dp[times][hp - 1] + dp[times - 1][hp]; + if (hp - 1 - M >= 0) { + dp[times][hp] -= dp[times - 1][hp - 1 - M]; } else { - dp[i][j] -= dp[i - M - 1][j - 1]; + dp[times][hp] -= Math.pow(M + 1, times - 1); } } } - return (double) ((double) dp[N][K] / (double) dp[0][K]); + long kill = dp[K][N]; + return (double) ((double) kill / (double) all); } public static void main(String[] args) { @@ -80,9 +86,9 @@ public static void main(String[] args) { int N = (int) (Math.random() * NMax); int M = (int) (Math.random() * MMax); int K = (int) (Math.random() * KMax); - double ans1 = right1(N, M, K); - double ans2 = right2(N, M, K); - double ans3 = dp(N, M, K); + double ans1 = right(N, M, K); + double ans2 = dp1(N, M, K); + double ans3 = dp2(N, M, K); if (ans1 != ans2 || ans1 != ans3) { System.out.println("Oops!"); break; diff --git a/src/class22/Code02_MinCoinsNoLimit.java b/src/class22/Code02_MinCoinsNoLimit.java index 4884d869..06e48435 100644 --- a/src/class22/Code02_MinCoinsNoLimit.java +++ b/src/class22/Code02_MinCoinsNoLimit.java @@ -6,10 +6,10 @@ public static int minCoins(int[] arr, int aim) { return process(arr, 0, aim); } + // arr[index...]面值,每种面值张数自由选择, + // 搞出rest正好这么多钱,返回最小张数 + // 拿Integer.MAX_VALUE标记怎么都搞定不了 public static int process(int[] arr, int index, int rest) { - if (rest < 0) { - return Integer.MAX_VALUE; - } if (index == arr.length) { return rest == 0 ? 0 : Integer.MAX_VALUE; } else { @@ -17,14 +17,39 @@ public static int process(int[] arr, int index, int rest) { for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { int next = process(arr, index + 1, rest - zhang * arr[index]); if (next != Integer.MAX_VALUE) { - ans = Math.min(ans, next + zhang); + ans = Math.min(ans, zhang + next); } } return ans; } } - public static int dp(int[] arr, int aim) { + public static int dp1(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int ans = Integer.MAX_VALUE; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + int next = dp[index + 1][rest - zhang * arr[index]]; + if (next != Integer.MAX_VALUE) { + ans = Math.min(ans, zhang + next); + } + } + dp[index][rest] = ans; + } + } + return dp[0][aim]; + } + + public static int dp2(int[] arr, int aim) { if (aim == 0) { return 0; } @@ -37,8 +62,10 @@ public static int dp(int[] arr, int aim) { for (int index = N - 1; index >= 0; index--) { for (int rest = 0; rest <= aim; rest++) { dp[index][rest] = dp[index + 1][rest]; - if (rest - arr[index] >= 0 && dp[index][rest - arr[index]] != Integer.MAX_VALUE) + if (rest - arr[index] >= 0 + && dp[index][rest - arr[index]] != Integer.MAX_VALUE) { dp[index][rest] = Math.min(dp[index][rest], dp[index][rest - arr[index]] + 1); + } } } return dp[0][aim]; @@ -77,8 +104,9 @@ public static void main(String[] args) { int[] arr = randomArray(N, maxValue); int aim = (int) (Math.random() * maxValue); int ans1 = minCoins(arr, aim); - int ans2 = dp(arr, aim); - if (ans1 != ans2) { + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + if (ans1 != ans2 || ans1 != ans3) { System.out.println("Oops!"); printArray(arr); System.out.println(aim); diff --git a/src/class22/Code03_SplitNumber.java b/src/class22/Code03_SplitNumber.java new file mode 100644 index 00000000..23193bce --- /dev/null +++ b/src/class22/Code03_SplitNumber.java @@ -0,0 +1,85 @@ +package class22; + +public class Code03_SplitNumber { + + // n为正数 + public static int ways(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + return process(1, n); + } + + // 上一个拆出来的数是pre + // 还剩rest需要去拆 + // 返回拆解的方法数 + public static int process(int pre, int rest) { + if (rest == 0) { + return 1; + } + if (pre > rest) { + return 0; + } + int ways = 0; + for (int first = pre; first <= rest; first++) { + ways += process(first, rest - first); + } + return ways; + } + + public static int dp1(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + int[][] dp = new int[n + 1][n + 1]; + for (int pre = 1; pre <= n; pre++) { + dp[pre][0] = 1; + dp[pre][pre] = 1; + } + for (int pre = n - 1; pre >= 1; pre--) { + for (int rest = pre + 1; rest <= n; rest++) { + int ways = 0; + for (int first = pre; first <= rest; first++) { + ways += dp[first][rest - first]; + } + dp[pre][rest] = ways; + } + } + return dp[1][n]; + } + + public static int dp2(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + int[][] dp = new int[n + 1][n + 1]; + for (int pre = 1; pre <= n; pre++) { + dp[pre][0] = 1; + dp[pre][pre] = 1; + } + for (int pre = n - 1; pre >= 1; pre--) { + for (int rest = pre + 1; rest <= n; rest++) { + dp[pre][rest] = dp[pre + 1][rest]; + dp[pre][rest] += dp[pre][rest - pre]; + } + } + return dp[1][n]; + } + + public static void main(String[] args) { + int test = 39; + System.out.println(ways(test)); + System.out.println(dp1(test)); + System.out.println(dp2(test)); + } + +} diff --git a/src/class22/Code03_SplitSumClosed.java b/src/class23/Code01_SplitSumClosed.java similarity index 97% rename from src/class22/Code03_SplitSumClosed.java rename to src/class23/Code01_SplitSumClosed.java index 85ce1473..605c930a 100644 --- a/src/class22/Code03_SplitSumClosed.java +++ b/src/class23/Code01_SplitSumClosed.java @@ -1,8 +1,8 @@ -package class22; +package class23; import java.util.TreeSet; -public class Code03_SplitSumClosed { +public class Code01_SplitSumClosed { public static int right(int[] arr) { if (arr == null || arr.length < 2) { diff --git a/src/class22/Code04_SplitSumClosedSizeHalf.java b/src/class23/Code02_SplitSumClosedSizeHalf.java similarity index 97% rename from src/class22/Code04_SplitSumClosedSizeHalf.java rename to src/class23/Code02_SplitSumClosedSizeHalf.java index 42d4d794..2930349b 100644 --- a/src/class22/Code04_SplitSumClosedSizeHalf.java +++ b/src/class23/Code02_SplitSumClosedSizeHalf.java @@ -1,8 +1,8 @@ -package class22; +package class23; import java.util.TreeSet; -public class Code04_SplitSumClosedSizeHalf { +public class Code02_SplitSumClosedSizeHalf { public static int right(int[] arr) { if (arr == null || arr.length < 2) { diff --git a/src/class23/Code01_NQueens.java b/src/class23/Code03_NQueens.java similarity index 98% rename from src/class23/Code01_NQueens.java rename to src/class23/Code03_NQueens.java index 7fd3cfbf..9264e0ca 100644 --- a/src/class23/Code01_NQueens.java +++ b/src/class23/Code03_NQueens.java @@ -1,6 +1,6 @@ package class23; -public class Code01_NQueens { +public class Code03_NQueens { public static int num1(int n) { if (n < 1) { diff --git a/src/class23/Code02_MinCoinsOnePaper.java b/src/class23/Code04_MinCoinsOnePaper.java similarity index 99% rename from src/class23/Code02_MinCoinsOnePaper.java rename to src/class23/Code04_MinCoinsOnePaper.java index 841b0ff4..bf482431 100644 --- a/src/class23/Code02_MinCoinsOnePaper.java +++ b/src/class23/Code04_MinCoinsOnePaper.java @@ -4,7 +4,7 @@ import java.util.Map.Entry; import java.util.LinkedList; -public class Code02_MinCoinsOnePaper { +public class Code04_MinCoinsOnePaper { public static int minCoins(int[] arr, int aim) { return process(arr, 0, aim);