代码随想录算法训练营第四十三天|1049. 最后一块石头的重量 II 、494. 目标和、474.一和零

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


题目链接:link

思路

这题在没有看题解时什么想法都没有,连暴露都不知道怎么写。看了题解后才恍然大悟,确实是动态规划,和 416. 分割等和子集合很像。
石头两两碰撞,那不就是可以均分成俩堆,然后俩堆互相碰撞,至于碰撞的过程可以不用考虑了。

代码

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int n = stones.length;
        int sum = 0;
        for(int num : stones) {
            sum += num;
        }
        int target = sum / 2;
        int[] dp = new int[target + 1];
        for(int i = 0; i < n; 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]*2;
    }
}

494.目标和


题目链接:link

思路

  • 一开始想着,这题不是上一题一样吗都是分成两种情况?但是仔细想想,这题问的是表达式的数量,那就要记录下每种情况了,难度就又上去了。
    先思考二维dp数组的含义,这里由于求的是数量,那边数组的值可以假设为数量,显然i表示第几个数,那么可以想到j表示当期的总和为j,dp[i][j]的含义是取到第i个数时表达式的值为j的数量。那么,最后的答案为dp[n][target]
  • 这样想还是想的太少了,做了才发现没有考虑到负数的情况,数组下表为负直接就报错了,接下来就要继续思考如何避免负数的情况。目前可以知道最后表达式的值为target,所有数之和为sum,那么有关系式正数之和-(sum-正数之和)= target,从而可以知道如果最后表达式为target,就必须所有的正数之和为(sum+targert)/2
  • 从而本题变成了当表达式中正数之和为(sum+targert)/2时有几种情况。此时dp[i][j]的含义为当取到第i个数时正数之和为j的表达式的数量为dp[i][j]

代码

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 < 0 && sum < -target) return 0;
        if ((target + sum) % 2 != 0) return 0;
        int size = (target + sum) / 2;
        if(size < 0) size = -size;
        int max=Math.max(Math.max(size+1,sum+1),Math.abs(target)+1);
        int[][] dp = new int[nums.length][max+1];
        dp[0][nums[0]]=1;
        dp[0][0]+=1;
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j <=max; j++) {
                if(j-nums[i]>=0){
                    dp[i][j]+=dp[i-1][j-nums[i]];
                }
                dp[i][j]+=dp[i-1][j];
                
            }
        }
        // for(int i=0;i<nums.length;i++){
        //     for(int j=0;j<size+1;j++)
        //     {
        //         System.out.print(dp[i][j]+" ,");
        //     }
        //     System.out.println();
            
        // }
        return dp[nums.length-1][size];
    }
}

总结

笑了,怎么会有这么多的问题,真的是苦了。
#474.一和零

题目链接:link

思路

这也太难了吧

代码

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        //dp[i][j]表示i个0和j个1时的最大子集
        int[][] dp = new int[m + 1][n + 1];
        int oneNum, zeroNum;
        for (String str : strs) {
            oneNum = 0;
            zeroNum = 0;
            for (char ch : str.toCharArray()) {
                if (ch == '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];
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值