Leetcode416/1049/494之01背包类型的例题
Leetcode416-分割等和子集
- 给你一个 只包含正整数 的 非空 数组
nums。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 - 输入:nums = [1,5,11,5]
- 输出:true
//二维数组版
public boolean canPartition(int[] nums) {
int sum = 0;
int maxNum = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
maxNum = Math.max(maxNum, nums[i]);
}
if (sum % 2 == 1 || nums.length == 1 || maxNum > sum / 2) {
return false;
}
int target = sum / 2;
boolean[][] dp = new boolean[nums.length][target + 1];
for (int i = 0; i < nums.length; i++) {
dp[i][0] = true;
}
dp[0][nums[0]] = true;//其余都默认false
for (int i = 1; i < nums.length; i++) {
int num = nums[i];
for (int j = 1; j <= target; j++) {
if (j >= num) {
dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[nums.length - 1][target];
}
//一维数组版
public boolean canPartition2(int[] nums) {
int sum = 0;
int maxNum = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
maxNum = Math.max(maxNum, nums[i]);
}
//先进行一遍筛选
if (sum % 2 == 1 || nums.length == 1 || maxNum > sum / 2) {
return false;
}
int target = sum / 2;
//定义dp数组
boolean[] dp = new boolean[target + 1];
//初始化
dp[0] = true;
//遍历
for (int i = 0; i < nums.length; i++) {
//因为是01背包问题 所以此处倒序
for (int j = target; j >= nums[i]; j--) {
dp[j] = dp[j] || dp[j - nums[i]];
}
}
return dp[target];
}
Leetcode1049-最后一块石头的重量二
- 有一堆石头,用整数数组
stones表示。其中stones[i]表示第i块石头的重量。 - 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为
x和y,且x <= y。那么粉碎的可能结果如下:- 如果
x == y,那么两块石头都会被完全粉碎; - 如果
x != y,那么重量为x的石头将会完全粉碎,而重量为y的石头新重量为y-x。
- 如果
- 最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回
0。 - 输入:stones = [2,7,4,1,8,1]
- 输出:1
//二维数组版
public int lastStoneWeightII(int[] stones) {
int totalWeight = 0;
for (int i = 0; i < stones.length; i++) {
totalWeight += stones[i];
}
int targetSum = totalWeight / 2;
int[][] dp = new int[stones.length][targetSum + 1];
for (int i = 0; i <= targetSum; i++) {
if (i >= stones[0]) {
dp[0][i] = stones[0];
}
}
for (int i = 1; i < stones.length; i++) {
int tempStone = stones[i];
for (int j = 1; j <= targetSum; j++) {
if (j >= tempStone) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - tempStone] + tempStone);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
int restSum = totalWeight - dp[stones.length - 1][targetSum];
return restSum - dp[stones.length - 1][targetSum];
}
//一维数组版
public int lastStoneWeightII2(int[] stones) {
int totalWeight = 0;
for (int i = 0; i < stones.length; i++) {
totalWeight += stones[i];
}
int targetSum = totalWeight / 2;
int[] dp = new int[targetSum + 1];
for (int i = 0; i < stones.length; i++) {
for (int j = targetSum; j >= stones[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return totalWeight - 2 * dp[targetSum];
}
Leetcode494-目标和
- 给你一个整数数组
nums和一个整数target。 - 向数组中的每个整数前添加
'+'或'-',然后串联起所有整数,可以构造一个 表达式 :- 例如,
nums = [2, 1],可以在2之前添加'+',在1之前添加'-',然后串联起来得到表达式"+2-1"。
- 例如,
- 返回可以通过上述方法构造的、运算结果等于
target的不同 表达式 的数目。 - 输入:nums = [1,1,1,1,1], target = 3
- 输出:5
//递归法
int res=0;
public int findTargetSumWays(int[] nums, int target) {
getSum(nums,target,0,0);
return res;
}
public void getSum(int[] nums,int target,int sum,int startIndex){
if(startIndex==nums.length){
if(sum==target){
res++;
}
return;
}
sum+=nums[startIndex];
getSum(nums,target,sum,startIndex+1);
sum-=nums[startIndex];
sum-=nums[startIndex];
getSum(nums,target,sum,startIndex+1);
sum+=nums[startIndex];
}
@Test
public void test(){
int[] nums=new int[]{1,1,1};
int target=1;
int targetSumWays = findTargetSumWays(nums, target);
System.out.println(targetSumWays);
}
//动态规划法
//假设加法的总和为x,那么减法对应的总和就是sum - x。
//所以我们要求的是 x - (sum - x) = S 即x = (S + sum) / 2
//此时问题就转化为,装满容量为x背包,有几种方法。
//此处还是排列问题 所以递归式为dp[j]+=dp[j-nums[i]];
public int findTargetSumWays2(int[] nums, int target) {
int sum=0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
if(target>sum || (target*-1)>sum){
return 0;
}
if((target+sum)%2==1){
return 0;
}
int tt=(target+sum)/2;
int[] dp=new int[tt+1];
dp[0]=1;
for(int i=0;i<nums.length;i++){
for(int j=tt;j>=nums[i];j--){
dp[j]+=dp[j-nums[i]];
}
}
return dp[tt];
}

浙公网安备 33010602011771号