思路:
把能否划分区间的问题转化为能不能找到一个使得和为总和的一半的子区间,然后转为01背包问题,分析每一个元素是否取
Partition problem is to determine whether a given set can be partitioned into two subsets such that the sum of elements in both subsets is same.
Examples
arr[] = {1, 5, 11, 5} Output: true The array can be partitioned as {1, 5, 5} and {11} arr[] = {1, 5, 3} Output: false The array cannot be partitioned into equal sum sets.
Following are the two main steps to solve this problem:
1) Calculate sum of the array. If sum is odd, there can not be two subsets with equal sum, so return false.
2) If sum of array elements is even, calculate sum/2 and find a subset of array with sum equal to sum/2.
The first step is simple. The second step is crucial, it can be solved either using recursion or Dynamic Programming.
Recursive Solution
Following is the recursive property of the second step mentioned above.
Let isSubsetSum(arr, n, sum/2) be the function that returns true if there is a subset of arr[0..n-1] with sum equal to sum/2 The isSubsetSum problem can be divided into two subproblems a) isSubsetSum() without considering last element (reducing n to n-1) b) isSubsetSum considering the last element (reducing sum/2 by arr[n-1] and n to n-1) If any of the above the above subproblems return true, then return true. isSubsetSum (arr, n, sum/2) = isSubsetSum (arr, n-1, sum/2) || isSubsetSum (arr, n-1, sum/2 - arr[n-1])
package DP; public class PartitionProbelm { public static void main(String[] args) { int[] A = {3, 1, 5, 9, 12}; int n = A.length; System.out.println(findPartitionRec(A, n)); System.out.println(findPartitionDP(A, n)); } // A utility function that returns true if there is a subset of arr[] // with sun equal to given sum public static boolean isSubsetSumRec(int[] A, int n, int sum){ if(sum < 0){ return false; } if(sum == 0){ return true; } if(n==0 && sum>0){ return false; } // if(A[n-1] > sum){ // If last element is greater than sum, then ignore it // return isSubsetSumRec(A, n-1, sum); // } /* else, check if sum can be obtained by any of the following (a) excluding the last element 不选A[n-1] (b) including the last element 选A[n-1] */ return isSubsetSumRec(A, n-1, sum) || isSubsetSumRec(A, n-1, sum-A[n-1]); } // Returns true if A[] can be partitioned in two subsets of // equal sum, otherwise false // Time Complexity: O(2^n) In worst case // , this solution tries two possibilities (whether to include or exclude) for every element. public static boolean findPartitionRec(int[] A, int n){ int sum = 0; // Calculate sum of the elements in array for(int i=0; i<n; i++){ sum += A[i]; } if(sum%2 != 0){ // If sum is odd, there cannot be two subsets with equal sum return false; } // Find if there is subset with sum equal to half of total sum return isSubsetSumRec(A, n, sum/2); } // Returns true if A[] can be partitioned in two subsets of // equal sum, otherwise false // Time Complexity: O(sum*n) // Auxiliary Space: O(sum*n) public static boolean findPartitionDP(int[] A, int n){ int sum = 0; for(int i=0; i<n; i++){ // Calculate sum of all elements sum += A[i]; } if(sum%2 != 0){ // If odd, then no chance return false; } // part[i][j]: 记录在长为j的数组里能否找到总和为i的子区间 boolean[][] part = new boolean[sum/2+1][n+1]; for(int i=0; i<=n; i++){ // initialize top row as true part[0][i] = true; // 如果总和为0,则肯定能找到(即一个都不选) } for(int i=1; i<=sum/2; i++){ // initialize leftmost column, except part[0][0], as false part[i][0] = false; // 如果总和为i,且数组长为0,则不可能找到 } // Fill the partition table in bottom up manner for(int i=1; i<=sum/2; i++){ // 要找总和为i for(int j=1; j<=n; j++){ // 给定数组长度为j part[i][j] = part[i][j-1]; // 不选A[j-1]的情况,更新数组长度为j-1 if(i-A[j-1] >= 0){ // 选择A[j-1]的情况,更新总和为i-A[j-1],也更新数组长度为j-1 part[i][j] = part[i][j] || part[i-A[j-1]][j-1]; } } } return part[sum/2][n]; } }
http://www.geeksforgeeks.org/dynamic-programming-set-18-partition-problem/