From b13a106fecdab1d977ffb7de81fbcfc12a11cb17 Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Fri, 25 Jul 2025 18:23:29 +0800 Subject: [PATCH] =?UTF-8?q?7.25=20=E5=8A=A8=E6=80=81=E8=A7=84=E5=88=92?= =?UTF-8?q?=E4=BA=8C=E5=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamic_programming/CanPartition.java | 74 ++++++------------- src/main/java/dynamic_programming/Change.java | 27 +++++++ .../java/dynamic_programming/CoinChange.java | 41 ++++++---- .../LastStoneWeightII.java | 16 ++++ .../java/dynamic_programming/LengthOfLIS.java | 17 +++++ .../java/dynamic_programming/MaxProduct.java | 14 ++++ .../java/dynamic_programming/NumSquares.java | 32 +++++--- src/main/java/dynamic_programming/Rob.java | 12 +-- src/main/java/dynamic_programming/Rob2.java | 30 ++++---- .../java/dynamic_programming/WordBreak.java | 18 ++++- .../dynamic_programming/CoinChangeTest.java | 4 +- .../LastStoneWeightIITest.java | 2 +- .../dynamic_programming/LengthOfLISTest.java | 13 ++++ .../dynamic_programming/NumSquaresTest.java | 13 ++++ 14 files changed, 210 insertions(+), 103 deletions(-) create mode 100644 src/test/java/dynamic_programming/LengthOfLISTest.java create mode 100644 src/test/java/dynamic_programming/NumSquaresTest.java diff --git a/src/main/java/dynamic_programming/CanPartition.java b/src/main/java/dynamic_programming/CanPartition.java index edb58fe..5e24c5e 100644 --- a/src/main/java/dynamic_programming/CanPartition.java +++ b/src/main/java/dynamic_programming/CanPartition.java @@ -1,6 +1,5 @@ package dynamic_programming; -import java.util.Arrays; /** * 题目: 416. 分割等和子集 (CanPartition) @@ -13,64 +12,33 @@ import java.util.Arrays; * 链接:https://leetcode.cn/problems/partition-equal-subset-sum/ */ +//二刷不会 public class CanPartition { - private void reverse(int[] nums) { - int left = 0, right = nums.length - 1; - while (left < right) { - int tmp = nums[left]; - nums[left] = nums[right]; - nums[right] = tmp; - left++; - right--; - } - } - private boolean backtrack(int[] nums, int target, int start) { - // 若已经完全匹配到目标和,返回 true - if (target == 0) { - return true; - } - - // 如果目标和已经小于0,说明该分支不可行,返回 false - if (target < 0) { - return false; - } - - // 从 start 开始尝试使用后续的每个数 - for (int i = start; i < nums.length; i++) { - // 若当前数字超过 target,则可以跳过 - if (nums[i] > target) { - continue; - } - // 选择当前数字,并尝试凑出剩余的 target - if (backtrack(nums, target - nums[i], i + 1)) { - return true; - } - } - return false; - } - //回溯法,超时了 public boolean canPartition1(int[] nums) { - // 计算数组总和 - int sum = 0; - for (int num : nums) { - sum += num; + int total=0,target=0; + for (int i = 0; i < nums.length; i++) { + total+=nums[i]; } - - // 如果总和为奇数,则不可能平分 - if (sum % 2 != 0) { - return false; + if(total%2==1)return false; + target=total/2; + int[] dp=new int[target+1]; + for (int i = 0; i < nums.length; i++) { + for (int j = target; j >= nums[i]; j--) { + dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]); + } } - - // 目标值为总和的一半 - int target = sum / 2; - - // 对数组进行排序,为了后续剪枝和高效回溯,建议降序排列 - Arrays.sort(nums); - reverse(nums); - // 从第0个位置开始回溯,寻找是否有子集和为target - return backtrack(nums, target, 0); + return dp[target]==target; } + + + + + + + + + public boolean canPartition(int[] nums) { if(nums == null || nums.length == 0) return false; int n = nums.length; diff --git a/src/main/java/dynamic_programming/Change.java b/src/main/java/dynamic_programming/Change.java index f4efaab..0a6eb15 100644 --- a/src/main/java/dynamic_programming/Change.java +++ b/src/main/java/dynamic_programming/Change.java @@ -1,4 +1,7 @@ package dynamic_programming; + +import java.util.Arrays; + /** * 题目: 518. 零钱兑换 II (change) * 描述:给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 @@ -17,7 +20,31 @@ package dynamic_programming; * 链接:https://leetcode.cn/problems/coin-change-ii/ */ +//二刷会做 public class Change { + public int change1(int amount, int[] coins) { + int[] dp=new int[amount+1]; + dp[0]=1; + for (int i = 0; i < coins.length; i++) { + for (int j = coins[i]; j <= amount; j++) { + dp[j]=dp[j]+dp[j-coins[i]]; + } + } + return dp[amount]; + } + + + + + + + + + + + + + public int change(int amount, int[] coins) { int[]dp=new int[amount+1]; dp[0]=1; diff --git a/src/main/java/dynamic_programming/CoinChange.java b/src/main/java/dynamic_programming/CoinChange.java index 57f695c..e16f4ae 100644 --- a/src/main/java/dynamic_programming/CoinChange.java +++ b/src/main/java/dynamic_programming/CoinChange.java @@ -5,7 +5,7 @@ import java.util.Arrays; /** * 题目: 322. 零钱兑换 (coinChange ) * 描述:给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 - * 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 + * 计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1 。 * 你可以认为每种硬币的数量是无限的。 示例 2: @@ -15,24 +15,39 @@ import java.util.Arrays; * 链接:https://leetcode.cn/problems/coin-change/ */ +//二刷不会 public class CoinChange { + /** + * 可以按照爬楼梯的思路来想,amount为目标台阶,每一步只能走coins数组中的台阶数,dp[i]表示到达第i级台阶所需的最小步数, + * 那么自然可以想到dp[i] = min( dp[ i-coins[j] ] )+1,其中i - coins[j] >= 0 且j∈[ 0, len(coins) ]。如果i - coins[j] < 0说明走不到目标台阶i,会超出台阶i。 + */ public int coinChange(int[] coins, int amount) { - int max = amount+1; + int max = amount + 1; int[] dp = new int[amount + 1]; - //初始化dp数组为最大值 - Arrays.fill(dp,max); - //当金额为0时需要的硬币数目为0 + Arrays.fill(dp, max); dp[0] = 0; - for (int i = 0; i < coins.length; i++) { - //正序遍历:完全背包每个硬币可以选择多次 - for (int j = coins[i]; j <= amount; j++) { - //只有dp[j-coins[i]]不是初始最大值时,该位才有选择的必要 - if (dp[j - coins[i]] != max) { - //选择硬币数目最小的情况 - dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); + for (int i = 1; i <= amount; i++) { + for (int j = 0; j < coins.length; j++) { + if (coins[j] <= i) { + dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); } } } - return dp[amount] == max ? -1 : dp[amount]; + return dp[amount] > amount ? -1 : dp[amount]; + } + + /** + * 完全背包 + */ + public int coinChange2(int[] coins, int amount) { + int []dp=new int[amount+1]; + Arrays.fill(dp,amount+1); + dp[0]=0; + for (int i = 0; i < coins.length; i++) { + for (int j = coins[i]; j <= amount; j++) { + dp[j]=Math.min(dp[j],dp[j-coins[i]]+1); + } + } + return dp[amount]==amount+1?-1:dp[amount]; } } diff --git a/src/main/java/dynamic_programming/LastStoneWeightII.java b/src/main/java/dynamic_programming/LastStoneWeightII.java index d9c57d2..adbc7b2 100644 --- a/src/main/java/dynamic_programming/LastStoneWeightII.java +++ b/src/main/java/dynamic_programming/LastStoneWeightII.java @@ -21,7 +21,23 @@ import java.util.Arrays; * 链接:https://leetcode.cn/problems/last-stone-weight-ii/ */ +//二刷会做 public class LastStoneWeightII { + public int lastStoneWeightII1(int[] stones) { + int total=0,target; + for (int stone : stones) { + total+=stone; + } + target=total/2; + int[] dp=new int[target+1]; + for (int i = 0; i < stones.length; i++) { + for (int j = target; j >= stones[i]; j--) { + dp[j]=Math.max(dp[j],dp[j-stones[i]]+stones[i]); + } + } + return Math.abs(total-2*dp[target]); + } + public int lastStoneWeightII(int[] stones) { int sum=0; for (int stone : stones) { diff --git a/src/main/java/dynamic_programming/LengthOfLIS.java b/src/main/java/dynamic_programming/LengthOfLIS.java index e044ac5..ae61f04 100644 --- a/src/main/java/dynamic_programming/LengthOfLIS.java +++ b/src/main/java/dynamic_programming/LengthOfLIS.java @@ -16,7 +16,24 @@ import java.util.PriorityQueue; * 链接:https://leetcode.cn/problems/longest-increasing-subsequence/ */ //没做出来 +//二刷会做 public class LengthOfLIS { + public int lengthOfLIS1(int[] nums) { + int length=nums.length; + int[] dp=new int[length]; + int MMax=1; + Arrays.fill(dp,1); + for (int i = 0; i < length; i++) { + for (int j = 0; j < i; j++) { + if(nums[i]>nums[j]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + MMax=Math.max(MMax,dp[i]); + } + } + } + return MMax; + } + public int lengthOfLIS(int[] nums) { if(nums.length == 0) return 0; int[] dp = new int[nums.length]; diff --git a/src/main/java/dynamic_programming/MaxProduct.java b/src/main/java/dynamic_programming/MaxProduct.java index c14fddc..2fe5b77 100644 --- a/src/main/java/dynamic_programming/MaxProduct.java +++ b/src/main/java/dynamic_programming/MaxProduct.java @@ -12,7 +12,21 @@ package dynamic_programming; * 链接:https://leetcode.cn/problems/maximum-product-subarray/ */ //不会 +//二刷不会 public class MaxProduct { + + /** + * 用动态规划维护遍历到当前位置时,以 nums[i] 结尾的「最大乘积」和「最小乘积」: + * - prevMax:前一步的最大乘积 + * - prevMin:前一步的最小乘积(因为负数相乘会翻转大小) + * 对于新元素 x = nums[i],候选值只有三种: + * 1. 单独以 x 为一段子数组 + * 2. 将 x 与 prevMax 相乘(延续最大乘积序列) + * 3. 将 x 与 prevMin 相乘(延续最小乘积序列,有可能翻转成最大) + * 因此通过 max(x, x*prevMax, x*prevMin) 得到新的 currMax, + * 同理用 min(x, x*prevMax, x*prevMin) 得到 currMin。 + * 然后更新全局答案 ans = max(ans, currMax)。 + */ public int maxProduct(int[] nums) { if (nums == null || nums.length == 0) { throw new IllegalArgumentException("数组不能为空"); diff --git a/src/main/java/dynamic_programming/NumSquares.java b/src/main/java/dynamic_programming/NumSquares.java index 2f87aec..96e1ffb 100644 --- a/src/main/java/dynamic_programming/NumSquares.java +++ b/src/main/java/dynamic_programming/NumSquares.java @@ -14,20 +14,34 @@ import java.util.Arrays; * 链接:https://leetcode.cn/problems/perfect-squares/ */ +//二刷会做 public class NumSquares { public int numSquares(int n) { - int[]dp=new int[n+1]; - double sqrted=Math.sqrt(n); - int soft_max=(int) sqrted; + int []dp=new int[n+1]; Arrays.fill(dp,n+1); - dp[0]=0; - for (int i = 1; i <= soft_max; i++) { - for (int j = 1; j <= n; j++) { - int cur= (int) Math.pow(i,2); - if(j>=cur &&dp[j-cur]!=n+1) - dp[j]=Math.min(dp[j],dp[(int) (j-Math.pow(i,2))]+1); + dp[0]= 0; + for (int i = 1; i <= n; i++) { + for (int j = 1; j*j<=i; j++) { + dp[i]=Math.min(dp[i],dp[i-j*j]+1); } } return dp[n]; } + +// public int numSquares(int n) { +// int[]dp=new int[n+1]; +// double sqrted=Math.sqrt(n); +// int soft_max=(int) sqrted; +// Arrays.fill(dp,n+1); +// dp[0]=0; +// for (int i = 1; i <= soft_max; i++) { +// for (int j = 1; j <= n; j++) { +// int cur= (int) Math.pow(i,2); +// if(j>=cur &&dp[j-cur]!=n+1) +// dp[j]=Math.min(dp[j],dp[(int) (j-Math.pow(i,2))]+1); +// } +// } +// return dp[n]; +// } +// } diff --git a/src/main/java/dynamic_programming/Rob.java b/src/main/java/dynamic_programming/Rob.java index 9ad7100..eff4058 100644 --- a/src/main/java/dynamic_programming/Rob.java +++ b/src/main/java/dynamic_programming/Rob.java @@ -12,18 +12,8 @@ package dynamic_programming; * 链接:https://leetcode.cn/problems/house-robber/ */ +//二刷会做 public class Rob { - public int rob1(int[] nums) { - int cnt=nums.length; - int[][]dp=new int[cnt][2]; - dp[0][0]=0; - dp[0][1]=nums[0]; - for (int i = 1; i < cnt; i++) { - dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]); - dp[i][1]=dp[i-1][0]+nums[i]; - } - return Math.max(dp[cnt-1][0],dp[cnt-1][1]); - } //推荐这 一维dp public int rob(int[] nums) { if (nums == null || nums.length == 0) return 0; diff --git a/src/main/java/dynamic_programming/Rob2.java b/src/main/java/dynamic_programming/Rob2.java index 8fadca4..d1ac6fe 100644 --- a/src/main/java/dynamic_programming/Rob2.java +++ b/src/main/java/dynamic_programming/Rob2.java @@ -11,27 +11,31 @@ package dynamic_programming; * 链接:https://leetcode.cn/problems/house-robber-ii/ */ +//二刷会做 public class Rob2 { public int rob(int[] nums) { int n = nums.length; if (n == 0) return 0; if (n == 1) return nums[0]; - // 情况一:不抢第一家,抢 [1..n-1] - int max1 = robLinear(nums, 1, n - 1); // 情况二:不抢最后一家,抢 [0..n-2] - int max2 = robLinear(nums, 0, n - 2); - return Math.max(max1, max2); + int result1 = robRange(nums, 0, n - 2); + // 情况三:不抢第一家,抢 [1..n-1] + int result2 = robRange(nums, 1, n - 1); + return Math.max(result1, result2); } - // 经典线性打家劫舍,区间 [start..end] - private int robLinear(int[] nums, int start, int end) { - int prev2 = 0; // dp[i-2] - int prev1 = 0; // dp[i-1] - for (int i = start; i <= end; i++) { - int curr = Math.max(prev1, prev2 + nums[i]); - prev2 = prev1; - prev1 = curr; + // 198. 打家劫舍 的逻辑,区间 [start..end] + private int robRange(int[] nums, int start, int end) { + if (start == end) { + return nums[start]; } - return prev1; + // dp 大小和 nums 一致,直接复用索引 + int[] dp = new int[nums.length]; + dp[start] = nums[start]; + dp[start + 1] = Math.max(nums[start], nums[start + 1]); + for (int i = start + 2; i <= end; i++) { + dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]); + } + return dp[end]; } } diff --git a/src/main/java/dynamic_programming/WordBreak.java b/src/main/java/dynamic_programming/WordBreak.java index 2edb542..a4268c0 100644 --- a/src/main/java/dynamic_programming/WordBreak.java +++ b/src/main/java/dynamic_programming/WordBreak.java @@ -1,11 +1,13 @@ package dynamic_programming; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; /** * 题目: 139. 单词拆分 (wordBreak) * 描述:给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 - * 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 + * 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以 重复 使用。 示例 2: 输入: s = "leetcode", wordDict = ["leet", "code"] @@ -14,7 +16,21 @@ import java.util.List; * 链接:https://leetcode.cn/problems/word-break/ */ +//二刷不会 public class WordBreak { + public boolean wordBreak1(String s, List wordDict) { + int len=s.length(); + boolean[] dp=new boolean[len+1]; + HashSetset=new HashSet<>(wordDict); + dp[0]=true; + for (int i = 1; i <= len; i++) { + for (int j = 0; j < i; j++) { + dp[i]=dp[i]||dp[j]&&set.contains(s.substring(j,i)); + } + } + return dp[len]; + } + public boolean wordBreak(String s, List wordDict) { int total=s.length(); boolean[] dp=new boolean[total+1]; diff --git a/src/test/java/dynamic_programming/CoinChangeTest.java b/src/test/java/dynamic_programming/CoinChangeTest.java index 329dcb5..6244ffe 100644 --- a/src/test/java/dynamic_programming/CoinChangeTest.java +++ b/src/test/java/dynamic_programming/CoinChangeTest.java @@ -9,8 +9,8 @@ public class CoinChangeTest { @Test public void coinChange() { CoinChange coinChange = new CoinChange(); - int[]coins = {2}; - int amount = 1; + int[]coins = {1,2,5}; + int amount = 5; int res=coinChange.coinChange(coins,amount); System.out.println(res); } diff --git a/src/test/java/dynamic_programming/LastStoneWeightIITest.java b/src/test/java/dynamic_programming/LastStoneWeightIITest.java index 90e4a23..f192c37 100644 --- a/src/test/java/dynamic_programming/LastStoneWeightIITest.java +++ b/src/test/java/dynamic_programming/LastStoneWeightIITest.java @@ -10,7 +10,7 @@ public class LastStoneWeightIITest { public void lastStoneWeightII() { LastStoneWeightII solution = new LastStoneWeightII(); int[]stones = {2,7,4,1,8,1}; - int res=solution.lastStoneWeightII(stones); + int res=solution.lastStoneWeightII1(stones); System.out.println(res); } } \ No newline at end of file diff --git a/src/test/java/dynamic_programming/LengthOfLISTest.java b/src/test/java/dynamic_programming/LengthOfLISTest.java new file mode 100644 index 0000000..b6d3c72 --- /dev/null +++ b/src/test/java/dynamic_programming/LengthOfLISTest.java @@ -0,0 +1,13 @@ +package dynamic_programming; + +import junit.framework.TestCase; + +public class LengthOfLISTest extends TestCase { + + public void testLengthOfLIS1() { + int[] a={1,3,6,7,9,4,10,5,6}; + LengthOfLIS solution = new LengthOfLIS(); + int res=solution.lengthOfLIS1(a); + System.out.println(res); + } +} \ No newline at end of file diff --git a/src/test/java/dynamic_programming/NumSquaresTest.java b/src/test/java/dynamic_programming/NumSquaresTest.java new file mode 100644 index 0000000..3dfa77a --- /dev/null +++ b/src/test/java/dynamic_programming/NumSquaresTest.java @@ -0,0 +1,13 @@ +package dynamic_programming; + +import junit.framework.TestCase; + +public class NumSquaresTest extends TestCase { + + public void testNumSquares() { + int n=12; + NumSquares numSquares = new NumSquares(); + int res=numSquares.numSquares(n); + System.out.println(res); + } +} \ No newline at end of file