【代码随想录训练营】Day43-动态规划

代码随想录训练营 Day43

今日任务

1049.最后一块石头的重量Ⅱ
494.目标和
474.一和零
语言:Java

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

链接:https://leetcode.cn/problems/last-stone-weight-ii/
问题是如何转化为背包问题的?参考视频
以最简单的3个物品为例,x1,x2和x3,其中x1>x2,x3>x1-x2,最后得到的结果就是x3-(x1-x2)=x3-x1+x2,也就是说题目相当于将所有数据分为两组,再对每组分别求和得到S1和S2,要我们返回S1和S2的最小差值,其中S2=sum-S1,S2-S1=sum-2×S1,为了让这个差值尽可能小,我们就要保证S1尽可能大,也就是让S1尽可能接近sum的一半。

class Solution {
    public int lastStoneWeightII(int[] stones) {
        //1.明确dp数组及下标含义: dp[j]代表容量为j的背包最大重量为dp[j]
        //2.递归公式: dp[j] = max(dp[j], dp[j-weight[i]] + value[i])
        //3.初始化: 0
        //4.遍历顺序: 外层循环物品,内层循环背包(从后向前循环)
        //5.打印dp数组
        
        //问题转化:将所有的石头分成重量尽可能相近的两堆

        int sum = 0;
        for(int i = 0; i < stones.length; i++){
            sum += stones[i];
        }
        int target = sum / 2;
        int[] dp = new int[1501];

        //weight[i]=value[i]=stones[i]
        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((sum - dp[target]) - dp[target]);
        //因为sum/2是向下取整,所以sum-dp[target]一定大于dp[target]
        return (sum - dp[target]) - dp[target];
    }
}

494. 目标和

链接:https://leetcode.cn/problems/target-sum/

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int count = 0;
        //error: 容量为j的背包最大价值为dp[j]
        //填满容量为j的背包有dp[j]种方法
        //分成两组,两组和分别为S1和S2
        //最终S1和S2的差值为target
        //让S1和S2相等,都等于(sum-target)/2
        int sum = 0;
        for(int i = 0; i < nums.length; i++){
            sum += nums[i];
        }
        if(Math.abs(target) > sum) return 0; //target可能为负
        if((sum - target) % 2 == 1) return 0; //两边和肯定不同,无法凑出式子
        //S1-S2=target
        //sum-S1*2=target
        int goal = (sum - target) / 2;
        int[] dp = new int[goal + 1];
        dp[0] = 1; //填满容量为0的背包有1种方法
        for(int i = 0; i < nums.length; i++){
            for(int j = goal; j >= nums[i]; j--){
            	//dp[j]: 不取当前物品有dp[j]种取法
            	//dp[j-nums[i]]: 取当前物品有dp[j-nums[i]]种取法
            	//将两个取法相加就是当前最多种取法
                dp[j] += dp[j - nums[i]]; //这里是求有多少种方法,所以不需要取max,直接相加即可
            }
        }
        return dp[goal];
    }
}

474. 一和零

链接:https://leetcode.cn/problems/ones-and-zeroes/

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        //本题使用二维dp数组
        //使用二维数组是因为物品的重量有两个维度
        //解法相当于01背包问题滚动数组的解法(遍历顺序)
        //本质还是在原来的基础上不断更新同一个dp数组,所以和一维滚动数组是相同的,要防止数据覆盖的问题
        //如果不希望数据覆盖,要使用三维数组
        //dp[i][j]代表最多i个0和j个1的最大子集长度为dp[i][j]
        int[][] dp = new int[m + 1][n + 1];
        //strs[i]的最小长度为1,所以对于最多0个0和0个1的情况,取法为0
        for(String str: strs){
            int zeroNum = 0;
            int oneNum = 0;
            for(int i = 0; i < str.length(); i++){
                if(str.charAt(i) == '0') zeroNum++;
                else oneNum++;
            }
            //相当于二维数组从右下角开始倒序遍历
            for(int i = m; i >= zeroNum; i--){
                for(int j = n; j >= oneNum; j--){
                    //dp[i][j]: 不取当前的字符串,子集长度不变
                    //dp[i-zeroNum][j-oneNum]+1: 取当前字符串,子集长度要加1
                    dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值