day42【代码随想录】动态规划之分割等和子集、最后一块石头的重量 II、目标和


前言

1、分割等和子集
2、最后一块石头的重量 II
3、目标和


一、分割等和子集(力扣416)

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
在这里插入图片描述
思路:
因为元素我们只能用一次,因此我们要使用的是01背包。
动规五部曲
1、确定dp数组以及下标的含义
dp[j]:背包总容量是j,放进物品后,背的最大重量为dp[j]。
本题中每一个元素的数值既是重量,也是价值。
2、确定递推公式
dp[j] = Math.max(dp[j] , dp[j - nums[i] ] + nums[i]);
3、初始化
dp[0]一定是0
同01背包,一维dp的初始化
4、遍历顺序
先遍历nums[]数组中的数,然后倒序遍历背包容量
5、打印dp[]数组
输入[1,5,11,5] 为例
在这里插入图片描述
dp[11] == 11,说明可以将这个数组分割成两个子集,使得两个子集的元素和相等。

class Solution {
    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;

        int target = sum/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]);
            }
        }
        if(dp[target]==target) return true;
        else return false;
    }
}

在这里插入图片描述

二、最后一块石头的重量 II(力扣1049)

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
在这里插入图片描述

思路: 尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。
因为元素我们只能用一次,因此我们要使用的是01背包。
动规五部曲
1、确定dp数组以及下标的含义
dp[j]:容量为j的背包,最多可以背最大重量为dp[j]。
本题中每一个元素的数值既是重量,也是价值。
2、确定递推公式
dp[j] = Math.max(dp[j] , dp[j - stones[i] ] + stones[i]);
3、初始化
dp[0]一定是0
同01背包,一维dp的初始化
4、遍历顺序
先遍历nums[]数组中的数,然后倒序遍历背包容量
5、打印dp[]数组
输入:[2,4,1,1],此时target = (2 + 4 + 1 + 1)/2 = 4 ,dp数组状态图如下:

在这里插入图片描述

最后dp[target]里是容量为target的背包所能背的最大重量。

那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum=0;
        for(int i=0;i<stones.length;i++){
            sum += stones[i];
        }
        int target = sum/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]);
            }
        }
        //那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]
        return sum - 2*dp[target];
    }
}

在这里插入图片描述

三、目标和(力扣494)

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
在这里插入图片描述
思路:
先转化为动态规划问题:
一波神奇的推导:
本题要使表达式结果为target,
left组合为“+”组合
right组合为“-”组合

既然为target,那么就一定有 left组合 - right组合 = target。

left + right = sum,sum是固定的。
right = sum - left
**left - (sum - left) = target 推导出==> left = (target + sum)/2 **
如果出现不能整除的情况也就是说明没有这个组合
target是固定的,sum是固定的,left就可以求出来。
此时问题就是在集合nums中找出和为left的组合。

这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。本题则是装满有几种方法。其实这就是一个组合问题了。

动规五部曲
1、确定dp数组以及下标的含义
dp[j]:填满j这么大容积的包,有dp[j]种方法
2、确定递推公式
只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。

例如:dp[j],j 为5,

  • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
  • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
  • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
  • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
  • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包

那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。

dp[j] += dp[j-nums[i]]
3、初始化
dp[0]一定是1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。
4、遍历顺序
先遍历nums[]数组中的数,然后倒序遍历背包容量
5、打印dp[]数组
输入:nums: [1, 1, 1, 1, 1], S: 3

bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4
在这里插入图片描述

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0 ;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
        }
        if((target+sum)%2!=0 )return 0;
        //如果target过大 sum将无法满足
        if ( target < 0 && sum < -target) return 0;
        int bagSize = (target+sum)/2;
        int[] dp = new int[Math.abs(bagSize)+1]; 
        dp[0]=1;
        for(int i=0;i<nums.length;i++){
            for(int j=bagSize;j>=nums[i];j--){
                dp[j] += dp[j-nums[i]]; 
            }
        }
        return dp[bagSize];
    }
}

在这里插入图片描述


总结

即一个商品如果可以重复多次放入是完全背包,而只能放入一次是01背包,写法还是不一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值