代码随想录第36天|动态规划

1049.最后一块石头的重量||

本题就和 昨天的 416. 分割等和子集 很像了,可以尝试先自己思考做一做。

视频讲解:动态规划之背包问题,这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II_哔哩哔哩_bilibili

代码随想录

代码随想录

target初始值是sum / 2 ,尽量让石头尽可能的分成重量相同的两堆,最后一堆石头的总重量是dp[target],另一堆就是sum - dp [ target ] ,

因为target = sum /2 是向下取整,所以sum - dp[target ] 一定大于 dp[target];

相撞后剩下最小的石头重量是sum - dp[target ] -dp[target].

代码

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = Arrays.stream(stones).sum();
        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]);
            }
        }
        return sum - dp[target]-dp[target];
    }
}

494.目标和

大家重点理解 递推公式:dp[j] += dp[j - nums[i]],这个公式后面的提问 我们还会用到。

视频讲解:动态规划之背包问题,装满背包有多少种方法?| LeetCode:494.目标和_哔哩哔哩_bilibili

代码随想录

 第一想法

背包大小为target,散列元素只能取一次,要么加要么减。回溯算法

代码随想录

分出加法一个集合,减法一个集合。加法集合 - 减法集合 = target

left + right = sum

left - right = target;

=> left = (target+sum)/2;集合中有哪些元素能凑成4

dp[j] :容量为j 有 dp[j]种方法

这里,我们可以把状态定义为dp【i】【j】,表示用数组中前i个元素组成和为j的方案数。那么状态转移方程就是:

dp【i】【j】 = dp【i-1】[j-nums【i】] + dp【i-1】[j+nums【i】]

\这个方程的意思是,如果我们要用前i个元素组成和为j的方案数,那么有两种选择:第i个元素取正号或者取负号。如果取正号,那么前i-1个元素就要组成和为j-nums【i】的方案数;如果取负号,那么前i-1个元素就要组成和为j+nums【i】的方案数。所以两种选择的方案数相加就是dp【i】【j】。

但是这样定义状态会导致空间复杂度过高,因为我们需要一个二维数组来存储所有可能的状态。所以我们可以对问题进行一些变换,把它转化为一个背包问题。

我们可以把数组中所有取正号的元素看作一个集合P,所有取负号的元素看作一个集合N。那么有:

sum(P) - sum(N) = target

sum(P) + sum(N) = sum(nums)

两式相加得:

sum(P) = (target + sum(nums)) / 2

也就是说,我们只需要找到有多少种方法可以从数组中选出若干个元素使得它们的和等于(target + sum(nums)) / 2即可。这就变成了一个经典的01背包问题。

所以我们可以把状态定义为dp【j】,表示用数组中若干个元素组成和为j的方案数。那么状态

转移方程就是:

dp【j】 = dp【j】 + dp[j - nums【i】]

这个方程的意思是,如果我们要用若干个元素组成和为j的方案数,那么有两种选择:不选第i个元素或者选第i个元素。如果不选第i个元素,那么原来已经有多少种方案数就不变;如果选第i个元素,那么剩下要组成和为j - nums【i】 的方案数就等于dp[j - nums【i】]。所以两种选择相加就是dp【j】。

但是在实现这个状态转移方程时,有一个细节需要注意:由于每次更新dp【j】都依赖于之前计算过得dp值(也就是说当前行依赖于上一行),所以我们必须从后往前遍历更新dp值(也就是说从右往左更新),否则会覆盖掉之前需要用到得值。

最后返回dp【bag_size】即可。

代码

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = Arrays.stream(nums).sum();
        if(Math.abs(target)>sum)return 0;
        if((target+sum)%2==1)return 0;
        int bagSize = (target+sum)/2;
        int[] dp = new int[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];
    }
}

 474.一和零

通过这道题目,大家先粗略了解, 01背包,完全背包,多重背包的区别,不过不用细扣,因为后面 对于 完全背包,多重背包 还有单独讲解。

视频讲解:动态规划之背包问题,装满这个背包最多用多少个物品?| LeetCode:474.一和零_哔哩哔哩_bilibili

代码随想录

代码

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp = new int[m+1][n+1];
        int oneNum,zeroNum;
        for (String str : strs) {
            oneNum = 0;
            zeroNum = 0;
            for (char c : str.toCharArray()) {
                if(c=='0')
                    zeroNum++;
                else
                    oneNum++;
                    
            }
            for(int i = m;i>=zeroNum;i--){
                for(int j = n;j>=oneNum;j--){
                    dp[i][j] =Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);
                }
            }
        }
        return dp[m][n];
    }
}

 

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值