416. Partition Equal Subset Sum

来源:互联网 发布:百度黑马程序员 编辑:程序博客网 时间:2024/05/21 06:57

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:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5]Output: trueExplanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: [1, 2, 3, 5]Output: falseExplanation: The array cannot be partitioned into equal sum subsets.
这道题类似于背包问题,动规原则取决于当前包背还是不背。在top solution有个比较好的解释:

This problem is essentially let us to find whether there are several numbers in a set which are able to sum to a specific value (in this problem, the value is sum/2).

Actually, this is a 0/1 knapsack problem, for each number, we can pick it or not. Let us assume dp[i][j] means whether the specific sum j can be gotten from the first i numbers. If we can pick such a series of numbers from 0-i whose sum is j, dp[i][j] is true, otherwise it is false.

Base case: dp[0][0] is true; (zero number consists of sum 0 is true)

Transition function: For each number, if we don't pick it, dp[i][j] = dp[i-1][j], which means if the first i-1 elements has made it to j, dp[i][j] would also make it to j (we can just ignore nums[i]). If we pick nums[i]. dp[i][j] = dp[i-1][j-nums[i]], which represents that j is composed of the current value nums[i] and the remaining composed of other previous numbers. Thus, the transition function is dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]]

talking is cheap:

public boolean canPartition(int[] nums) {    int sum = 0;        for (int num : nums) {        sum += num;    }        if ((sum & 1) == 1) {        return false;    }    sum /= 2;    int n = nums.length;    boolean[][] dp = new boolean[n+1][sum+1];    for (int i = 0; i < dp.length; i++) {        Arrays.fill(dp[i], false);    }        dp[0][0] = true;        for (int i = 1; i < n+1; i++) {        dp[i][0] = true;    }    for (int j = 1; j < sum+1; j++) {        dp[0][j] = false;    }        for (int i = 1; i < n+1; i++) {        for (int j = 1; j < sum+1; j++) {            dp[i][j] = dp[i-1][j];            if (j >= nums[i-1]) {                dp[i][j] = (dp[i][j] || dp[i-1][j-nums[i-1]]);            }        }    }       return dp[n][sum];}

But can we optimize it? It seems that we cannot optimize it in time. But we can optimize in space. We currently use two dimensional array to solve it, but we can only use one dimensional array.

So the code becomes:

public boolean canPartition(int[] nums) {    int sum = 0;        for (int num : nums) {        sum += num;    }        if ((sum & 1) == 1) {        return false;    }    sum /= 2;        int n = nums.length;    boolean[] dp = new boolean[sum+1];    Arrays.fill(dp, false);    dp[0] = true;        for (int num : nums) {        for (int i = sum; i > 0; i--) {            if (i >= num) {                dp[i] = dp[i] || dp[i-num];            }        }    }        return dp[sum];}
还有一种更快的方法,类似于二分法:

public class Solution {    public boolean canPartition(int[] nums) {        int sum = 0;        for (int i = 0; i < nums.length; i++) {            sum += nums[i];        }        if (sum % 2 != 0) {            return false;        }        for (int i = 0; i < nums.length; i++) {            int tmp = nums[i];            if (tmp == sum / 2) {                return true;            }            for (int j = i + 1; j < nums.length; j++) {                tmp += nums[j];                if (tmp == sum / 2) {                    return true;                } else if (tmp > sum / 2) {                    tmp = tmp - nums[j];                }            }        }        return false;    }}

0 0
原创粉丝点击