7.25 动态规划二刷
This commit is contained in:
parent
c323d88ebc
commit
b13a106fec
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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("数组不能为空");
|
||||
|
@ -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];
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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<String> wordDict) {
|
||||
int len=s.length();
|
||||
boolean[] dp=new boolean[len+1];
|
||||
HashSet<String>set=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<String> wordDict) {
|
||||
int total=s.length();
|
||||
boolean[] dp=new boolean[total+1];
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
13
src/test/java/dynamic_programming/LengthOfLISTest.java
Normal file
13
src/test/java/dynamic_programming/LengthOfLISTest.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
13
src/test/java/dynamic_programming/NumSquaresTest.java
Normal file
13
src/test/java/dynamic_programming/NumSquaresTest.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user