leecode更新
This commit is contained in:
parent
0fcc05eba4
commit
09b670943b
|
|
@ -0,0 +1,138 @@
|
|||
package com.markilue.leecode.dynamic;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @BelongsProject: Leecode
|
||||
* @BelongsPackage: com.markilue.leecode.dynamic
|
||||
* @Author: markilue
|
||||
* @CreateTime: 2022-11-25 10:06
|
||||
* @Description: TODO 力扣416题 分割等和子集:
|
||||
* 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class T08_CanPartition {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
int[] nums = {1, 5, 11, 5};
|
||||
int[] nums1 = {1, 2, 3, 5};
|
||||
System.out.println(canPartition1(nums));
|
||||
}
|
||||
|
||||
/**
|
||||
* 自己思路:这题使用背包问题来分析的话,可以发现背包的最大容量可以确定--所有数之和除2,如果是奇数直接false如果是偶数转为背包问题
|
||||
* 1)首先:是不是0-1背包问题?虽然数组里的数可以有重复的,但是重复的数据也是彼此独立的,不是说一个数想用几次就用几次,所以是0-1背包问题
|
||||
* 2)其次:数组需不需要先进行排序?应该是不需要的,无论是动规的状态转移方程或者是其他的都是max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]),不需要一瞬加起来
|
||||
* 3)最后:背包容量的确定?对数组进行求和,取一半就是背包容量
|
||||
* 速度超过61.4%,内存超过5% 24ms
|
||||
*
|
||||
* @param nums
|
||||
* @return
|
||||
*/
|
||||
public boolean canPartition(int[] nums) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
sum += nums[i];
|
||||
}
|
||||
if (sum % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
//先使用二维dp尝试一次
|
||||
int[][] dp = new int[nums.length][sum / 2 + 1];
|
||||
|
||||
//dp数组的初始化
|
||||
//dp[i][0]=0当sum为0时都等于0;dp[0][i]
|
||||
for (int j = 0; j < dp[0].length; j++) {
|
||||
if (j >= nums[0]) dp[0][j] = nums[0];
|
||||
}
|
||||
for (int i = 1; i < nums.length; i++) {
|
||||
for (int j = 1; j < dp[0].length; j++) {
|
||||
if (j < nums[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
|
||||
if (dp[i][j] == sum / 2) return true;
|
||||
}
|
||||
}
|
||||
return dp[nums.length - 1][sum / 2] == sum / 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自己思路:尝试背包问题的滚动窗口法
|
||||
* 速度超过76.11%,内存超过58.54% 22ms
|
||||
* 时间复杂度O(n^2)空间复杂度O(n)
|
||||
* for循环优化以后速度超过67.3%,内存超过94.32%
|
||||
* @param nums
|
||||
* @return
|
||||
*/
|
||||
public boolean canPartition1(int[] nums) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
sum += nums[i];
|
||||
}
|
||||
if (sum % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
//先使用二维dp尝试一次
|
||||
int target=sum/2;
|
||||
int[] dp = new int[target + 1];
|
||||
|
||||
//dp数组的初始化
|
||||
//dp[i][0]=0当sum为0时都等于0;dp[0][i]
|
||||
|
||||
// for (int i = 0; i < nums.length; i++) {
|
||||
// for (int j = dp.length - 1; j >= 1; j--) {
|
||||
// if(j-nums[i]>=0)dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
return dp[target] == target;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 官方代码:与自己的背包滚动窗口类似,但是他是用true和false来判定这次能不能取:
|
||||
* TODO 动态规划法五部曲分析:
|
||||
* 1)dp数组的含义分析:dp[i][j]的含义表示为下标[0,i]这个区间里的所有整数,在他们当中是否能够选出一些数,使得这些数之和恰好为整数j
|
||||
* 2)dp数组的初始化方式:dp[i][0]=true;因为[0,0]这个区间只要所有数都不选,那么就可以
|
||||
* 速度击败93.89%,内存击败68.97% 18ms
|
||||
* @param nums
|
||||
* @return
|
||||
*/
|
||||
public boolean canPartition2(int[] nums) {
|
||||
int n = nums.length;
|
||||
if (n < 2) {
|
||||
return false;
|
||||
}
|
||||
int sum = 0, maxNum = 0;
|
||||
for (int num : nums) {
|
||||
sum += num;
|
||||
maxNum = Math.max(maxNum, num);
|
||||
}
|
||||
if (sum % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
int target = sum / 2;
|
||||
if (maxNum > target) {
|
||||
return false;
|
||||
}
|
||||
boolean[] dp = new boolean[target + 1];
|
||||
dp[0] = true;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int num = nums[i];
|
||||
for (int j = target; j >= num; --j) {
|
||||
dp[j] |= dp[j - num];
|
||||
}
|
||||
}
|
||||
return dp[target];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.markilue.leecode.dynamic;
|
||||
|
||||
/**
|
||||
* @BelongsProject: Leecode
|
||||
* @BelongsPackage: com.markilue.leecode.dynamic
|
||||
* @Author: markilue
|
||||
* @CreateTime: 2022-11-25 13:22
|
||||
* @Description:
|
||||
* TODO 力扣1049题 最后一块石头的重量II:
|
||||
* 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
|
||||
* 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
|
||||
* 如果 x == y,那么两块石头都会被完全粉碎;
|
||||
* 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
|
||||
* 最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class T09_LastStoneWeightII {
|
||||
|
||||
/**
|
||||
* 自己尝试动态规划法:
|
||||
* TODO 动态规划五部曲:
|
||||
* 1)确定dp的含义:使用[0-i]个石头,剩下的重量j的dp[i][j]ture或false
|
||||
* 2)确定状态转移方程:dp[i][j]= dp[i-1][j-num[i]] ||
|
||||
* 3)确定初始值:dp[0][j==num[0]]=true
|
||||
* 4)确定遍历方向:for石头再for重量
|
||||
* 5)举例说明:当stones = [2,7,4,1,8,1]
|
||||
* [0 1 2 3 4 5 6 7 8]
|
||||
* i=0: 0 0 1 0 0 0 0 0 0
|
||||
* i=1: 0 0 0 0 0 1 0 0 0
|
||||
* i=2: 0 1 0 0 0 1 0 0 0
|
||||
* i=3: 1 0 1 0 1 0 1 0 0
|
||||
* 推导失败
|
||||
* @param stones
|
||||
* @return
|
||||
*/
|
||||
public int lastStoneWeightII(int[] stones) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 代码随想录动态规划法:本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。
|
||||
* TODO 动态规划五部曲:核心是第六点,本题通过6)将问题转为之前的T08分割等和数组
|
||||
* 1)确定dp的含义:dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背dp[j]这么重的石头。
|
||||
* 2)确定状态转移方程::dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
|
||||
* 3)确定初始值:1 <= stones.length <= 30,1 <= stones[i] <= 1000,所以最大重量就是30 * 1000
|
||||
* 而我们要求的target其实只是最大重量的一半,所以dp数组开到15000大小就可以了。
|
||||
* 4)确定遍历方向:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
|
||||
* 5)举例说明:当stones = [2,4,1,1] target=4
|
||||
* [0 1 2 3 4]
|
||||
* i=0: 0 0 2 2 2
|
||||
* i=1: 0 0 2 2 4
|
||||
* i=2: 0 1 2 3 4
|
||||
* i=3: 0 1 2 3 4
|
||||
* 6)最后dp[target]里是容量为target的背包所能背的最大重量。那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。
|
||||
* 那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。
|
||||
* 速度击败45.51%,内存击败46.8% 时间复杂度O(n*sum)
|
||||
* @param stones
|
||||
* @return
|
||||
*/
|
||||
public int lastStoneWeightII1(int[] stones) {
|
||||
int sum = 0;
|
||||
for (int i : stones) {
|
||||
sum += i;
|
||||
}
|
||||
int target = sum >> 1;
|
||||
//初始化dp数组
|
||||
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 sum - 2 * dp[target];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue