diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T14_ClimbStairs.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T14_ClimbStairs.java new file mode 100644 index 0000000..c56e849 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T14_ClimbStairs.java @@ -0,0 +1,38 @@ +package com.markilue.leecode.dynamic.second; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic.second + *@Author: markilue + *@CreateTime: 2023-02-19 13:41 + *@Description: + * TODO 力扣70 爬楼梯(进阶版): + * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 + * 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? + *@Version: 1.0 + */ +public class T14_ClimbStairs { + + /** + * 思路:可以看做是一个完全背包问题: 背包大小是n,nums为[1,2]; + * 由于爬楼梯先爬1后爬2和先爬2后爬1是不一样的结果;所以遍历nums需要放里面 + * 速度击败100% 内存击败27.4% + * @param n + * @return + */ + public int climbStairs(int n) { + + int[] dp = new int[n + 1]; + dp[0] = 1; + + + for (int j = 0; j < dp.length; j++) { + for (int i = 1; i <= 2; i++) {//可以走1或者2 + if (j >= i) dp[j] = dp[j - i] + dp[j]; + } + } + return dp[n]; + + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T15_CoinChange.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T15_CoinChange.java new file mode 100644 index 0000000..c6dddd5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T15_CoinChange.java @@ -0,0 +1,116 @@ +package com.markilue.leecode.dynamic.second; + +import org.junit.Test; + +import java.util.Arrays; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic.second + *@Author: markilue + *@CreateTime: 2023-02-19 13:58 + *@Description: + * TODO 力扣322题 零钱兑换: + * 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 + * 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 + * 你可以认为每种硬币的数量是无限的。 + *@Version: 1.0 + */ +public class T15_CoinChange { + + @Test + public void test() { + int[] coins = {1, 2, 5}; + int amount = 11; + System.out.println(coinChange(coins, amount)); + } + + @Test + public void test1() { + int[] coins = {2}; + int amount = 3; + System.out.println(coinChange1(coins, amount)); + } + + @Test + public void test2() { + int[] coins = {2, 5, 10, 1}; + int amount = 27; + System.out.println(coinChange(coins, amount)); + } + + @Test + public void test3() { + int[] coins = {186,419,83,408}; + int amount = 6249; + System.out.println(coinChange(coins, amount)); + } + + + /** + * 思路:从一共有多少种方法解决,变成了这些方法中最少可以用多少种: + * TODO dp五部曲: + * 1.dp定义: dp[i][j]表示使用nums[0-i]凑出j的使用最少货币数 + * 2.dp状态转移方程: dp[i][j]可以选择 再多使用当前nums[i]一次 或者 不适用当前nums[i] + * dp[i][j]=min(dp[i][j-nums[i]]+1,dp[i-1][j]) + * 3.dp初始化:dp[i][0]=0 + * 4.dp遍历顺序:求的组合数;所以nums在外 + * 5.dp举例推导:coins = [1, 2, 5], amount = 11 + * [0 1 2 3 4 5 6 7 8 9 10 11] + * i=1: 0 1 2 3 4 5 6 7 8 9 10 11 + * i=2: 0 1 1 2 2 3 3 4 4 5 5 6 + * i=5: 0 1 1 2 2 1 2 2 3 3 2 3 + * 速度击败23.5% + * @param coins + * @param amount + * @return + */ + public int coinChange(int[] coins, int amount) { + + int[][] dp = new int[coins.length][amount + 1]; + Arrays.sort(coins); + for (int[] d : dp) { + Arrays.fill(d, Integer.MAX_VALUE); //注意这里必须是Max_Value,不能是Min_Value,否则Min可能取不到 + } + + dp[0][0] = 0; + for (int i = 1; i < dp[0].length; i++) { + if (i >= coins[0] && dp[0][i - coins[0]] != Integer.MAX_VALUE) dp[0][i] = dp[0][i - coins[0]] + 1; + } + + for (int i = 1; i < coins.length; i++) { + for (int j = 0; j < dp[0].length; j++) { + if (j >= coins[i] && dp[i][j - coins[i]] != Integer.MAX_VALUE) + dp[i][j] = Math.min(dp[i][j - coins[i]] + 1, dp[i - 1][j]); + else dp[i][j] = dp[i - 1][j]; + } + } + + return dp[coins.length - 1][amount] == Integer.MAX_VALUE ? -1 : dp[coins.length - 1][amount]; + + } + + + /** + * 一维dp优化 + * @param coins + * @param amount + * @return + */ + public int coinChange1(int[] coins, int amount) { + + int[] dp = new int[amount + 1]; + Arrays.fill(dp,Integer.MAX_VALUE); + dp[0]=0; + + for (int i = 0; i < coins.length; i++) { + for (int j = coins[i]; j < dp.length; j++) { + if (dp[j-coins[i]]!=Integer.MAX_VALUE) + dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]); + } + } + + return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount]; + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T16_NumSquares.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T16_NumSquares.java new file mode 100644 index 0000000..da7536a --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T16_NumSquares.java @@ -0,0 +1,100 @@ +package com.markilue.leecode.dynamic.second; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic.second + *@Author: markilue + *@CreateTime: 2023-02-19 14:55 + *@Description: + * TODO 力扣279 完全平方数: + * 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。 + * 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。 + *@Version: 1.0 + */ +public class T16_NumSquares { + + @Test + public void test() { +// int t = 10; +// System.out.println(Math.sqrt(t)); +// System.out.println((int) Math.sqrt(t)); +// System.out.println((int) Math.sqrt(9) == Math.sqrt(9)); + System.out.println(numSquares(12)); + } + + /** + * 思路:与上一个零钱问题类似,事实上零钱问题给出了可用的零钱数;而这一题需要判断是否是可用的零钱, + * TODO dp五部曲: + * 1.dp定义:dp[j]表示使用完全平方数能凑出的最少个数 + * 2.dp状态转移方程:dp[j]=min(dp[j],dp[j-完全]) + * 3.dp初始化:dp[0]=0; + * 4.dp遍历顺序:都可以 + * 5.dp状态转移方程: n=12: + * [0 1 2 3 4 5 6 7 8 9 10 11 12] + * i=1: 0 1 2 3 4 5 6 7 8 9 10 11 12 + * i=4: 0 1 2 3 1 2 3 4 2 3 4 5 3 + * i=9: 0 1 2 3 1 2 3 4 2 1 2 3 3 + * 速度击败9.4% 内存击败37.54% 157ms + * @param n + * @return + */ + public int numSquares(int n) { + List list = findSquares(n); + + int[] dp = new int[n + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + dp[0] = 0; + + for (int i = 0; i < list.size(); i++) { + for (int j = list.get(i); j < dp.length; j++) { + if (dp[j - list.get(i)] != Integer.MAX_VALUE) dp[j] = Math.min(dp[j], dp[j - list.get(i)] + 1); + } + } + + return dp[n]; + + } + + public List findSquares(int n) { + List list = new ArrayList<>(); + + for (int i = 1; i <= n; i++) { + if ((int) Math.sqrt(i) == Math.sqrt(i)) { + list.add(i); + } + } + return list; + } + + /** + * 官方一维dp,避免判断完全平方数 + * 速度击败65.5% 内存击败44.71% + * @param n + * @return + */ + public int numSquares1(int n) { + int max = n + 1; //因为最少可以用1凑n,最多只需要n个数字 + int[] dp = new int[n + 1]; + dp[0] = 0; + for (int i = 1; i < max; i++) { + dp[i] = max; + } + int maxSqrt = (int) Math.sqrt(n); + + for (int i = 1; i <= maxSqrt; i++) { + for (int j = i * i; j < n + 1; j++) { + if (dp[j - i * i] != max) { + dp[j] = Math.min(dp[j], dp[j - i * i] + 1); + } + } + } + + return dp[n]; + } +}