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 块石头的重量。
  • 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 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];
    }
posted @ 2022-04-18 13:28  fao99  阅读(33)  评论(0)    收藏  举报