Partition Equal Subset Sum

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Note:
Each of the array element will not exceed 100.
The array size will not exceed 200.

題意:給定一個非空非負的數組,判斷其能否分成元素和大小相等的兩個子集。

分析: 那我第一個想法就是遞歸搜索所有情況,一個元素要麼在sub1 要麼在sub2,即遍歷一次數組,對每個元素採取 “取”或“不取”兩種操作,遍歷完後 判斷sub1的和是否等於sub2的和。

暴力遞歸:超時
    public boolean canPartition(int[] nums) {
        if(nums == null || nums.length == 0)
            return true;

        int sum = 0;
        for (int x : nums)
            sum += x;

        return dfs(nums,0,0,sum);
    }

    public boolean dfs(int[] nums,int start , int left,int right)
    {
        if(start == nums.length || left == right)
        {
            if(left == right)
                return true;
            else
                return false;
        }

        return dfs(nums,start+1,left+nums[start],right-nums[start])||dfs(nums,start+1,left,right);
    }

如何優化,是否可以記憶化:
其實已經想到這個問題,可以轉化爲判斷是否存在一個子集,它的和爲一個目標值(在這裏爲totalSum/2),這樣一但總和爲奇數也可直接判斷。
那對於這樣一個問題,感覺和Sum2以及Sum3很像。但是不一樣,Sum2指定了只能選擇兩個數達到target,而這裏可以遍歷整個數組,對每個數有要 和 不要 兩種選擇,說到這裏是否感覺和01揹包問題很像。
對了,這道題的實戰就是一道01揹包問題,正好題目也限制了元素的個數和大小,不至於過大!!!

那01揹包是最基礎的一道DP問題,給定揹包的容量,讓我們從物品中選出價值最大的組合。

而這裏,問題實際上就是給定一個容量,讓我們判斷是否有一種組合剛好放滿揹包。

狀態定義:dp[i][j]表示前i個物品是否存在子集和爲j
狀態轉移方程:dp[i][j] = dp[i-1][j] || dp[i-1][j-v[i]]

 public boolean canPartition(int[] nums) {
        if(nums == null || nums.length == 0)
            return true;

        int sum = 0;
        for (int x : nums)
            sum += x;

        if (sum % 2 == 1)
            return false;

        int tar = sum / 2;
        boolean[][] dp = new boolean[nums.length+1][tar+1];
        dp[0][0] = true;
        for (int i = 1 ; i < dp.length ; i++)
            dp[i][0] = nums[i-1]==0 && dp[i-1][0];

        for (int i = 1 ; i < dp.length ; i ++)
        {
            for (int j = 1; j <= tar ; j++)
            {

                if (nums[i-1] <= j)
                    dp[i][j] = dp[i-1][j-nums[i-1]] || dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j];
            }
        }


        return dp[nums.length][tar];
    }

01揹包還可以進行空間優化,一維數組,從後往前遍歷。

    //一維揹包,此時當前dp[j]表示對於前i個物品容量能否達到j
    //之所以從後往前,是因爲下一層需要用到dp[j-nums[i]],避免被更新
    public boolean betterdp_canPartition(int[] nums) {
        if(nums == null || nums.length == 0)
            return true;

        int sum = 0;
        for (int x : nums)
            sum += x;

        if (sum % 2 == 1)
            return false;

        int tar = sum / 2;

        boolean[] dp = new boolean[tar +1];

        dp[0] = true;

        for (int i = 1 ; i < nums.length ; i ++)
        {
            for (int j = tar; j >= 0 ; j--)
            {

                if (nums[i-1] <= j)
                    dp[j] = dp[j-nums[i-1]] || dp[j];
            }
        }


        return dp[tar];
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章