1. 程式人生 > 其它 >Leetcode416/1049/494之01揹包型別的例題

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];
    }