您的位置:首页 > 其它

动态规划之分区问题(Partition problem)

2016-10-24 18:12 609 查看
原文地址:Dynamic Programming | Set 18 (Partition problem)

分区问题是将已知的集合分成两个子集,这两个子集的元素分别加和是相等的。

例子:

arr[] = {1, 5, 11, 5}
Output: true
这个数组可以被分为:{1, 5, 5}和{11}

arr[] = {1, 5, 3}
Output: false
这个数组不能被分成两个和相同的子集.


根据下面的两个步骤来解决这个问题:

1、计算数组的所有元素的和,如果和是奇数,那么就不能分成两个和相等的子集,返回false。

2、如果所有元素的和是偶数,那就算一下sum/2,然后找出一个子集让这个子集的所有元素和等于sum/2。

第一步比较简单,第二步才是关键,第二步可以用递归或者动态规划来解决。

递归方法:

下面是上述第二步的递归属性。

设这个函数为isSubsetSum(arr, n, sum/2),如果arr[0..n-1]有一个子集的和等于sum/2。

isSubsetSum问题可以分成两个子问题:
a)isSubsetSum()不考虑最后一个元素(减少n到n-1);
b)isSubsetSum 考虑最后一个元素(sum/2减去arr[n-1],减少n到n-1)

如果以上两条任何一个子问题返回true,那么整个方法返回true。
isSubsetSum (arr, n, sum/2) = isSubsetSum (arr, n-1, sum/2) || isSubsetSum (arr, n-1, sum/2 - arr[n-1])


// A recursive Java solution for partition problem
import java.io.*;

class Partition
{
// A utility function that returns true if there is a
// subset of arr[] with sun equal to given sum
static boolean isSubsetSum (int arr[], int n, int sum)
{
// Base Cases
if (sum == 0)
return true;
if (n == 0 && sum != 0)
return false;

// If last element is greater than sum, then ignore it
if (arr[n-1] > sum)
return isSubsetSum (arr, n-1, sum);

/* else, check if sum can be obtained by any of
the following
(a) including the last element
(b) excluding the last element
*/
return isSubsetSum (arr, n-1, sum) ||
isSubsetSum (arr, n-1, sum-arr[n-1]);
}

// Returns true if arr[] can be partitioned in two
// subsets of equal sum, otherwise false
static boolean findPartition (int arr[], int n)
{
// Calculate sum of the elements in array
int sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];

// If sum is odd, there cannot be two subsets
// with equal sum
if (sum%2 != 0)
return false;

// Find if there is subset with sum equal to half
// of total sum
return isSubsetSum (arr, n, sum/2);
}

/*Driver function to check for above function*/
public static void main (String[] args)
{

int arr[] = {3, 1, 5, 9, 12};
int n = arr.length;
if (findPartition(arr, n) == true)
System.out.println("Can be divided into two "+
"subsets of equal sum");
else
System.out.println("Can not be divided into " +
"two subsets of equal sum");
}
}
/* This code is contributed by Devesh Agrawal */


输出:

Can be divided into two subsets of equal sum


最坏情况下的时间复杂度是:O(2^n) ,这种解决方法对于每一个元素都尝试了两种可能(是否包含)。

动态规划方法

当数组中元素的和太大的时候,这个问题就可以用动态规划的方法来解决了。我们可以建立一个大小为(sum/2)*(n+1)的二维数组part[][]。我们可以用自底向上的方法构建这个方法,这样的话每一个entry都有符合下面的式子:

part[i][j] = true if a subset of {arr[0], arr[1], ..arr[j-1]} has sum equal to i, otherwise false


// A dynamic programming based Java program for partition problem
import java.io.*;

class Partition {

// Returns true if arr[] can be partitioned in two subsets of
// equal sum, otherwise false
static boolean findPartition (int arr[], int n)
{
int sum = 0;
int i, j;

// Caculcate sun of all elements
for (i = 0; i < n; i++)
sum += arr[i];

if (sum%2 != 0)
return false;

boolean part[][]=new boolean[sum/2+1][n+1];

// initialize top row as true
for (i = 0; i <= n; i++)
part[0][i] = true;

// initialize leftmost column, except part[0][0], as 0
for (i = 1; i <= sum/2; i++)
part[i][0] = false;

// Fill the partition table in botton up manner
for (i = 1; i <= sum/2; i++)
{
for (j = 1; j <= n; j++)
{
part[i][j] = part[i][j-1];
if (i >= arr[j-1])
part[i][j] = part[i][j] ||
part[i - arr[j-1]][j-1];
}
}

/* // uncomment this part to print table
for (i = 0; i <= sum/2; i++)
{
for (j = 0; j <= n; j++)
printf ("%4d", part[i][j]);
printf("\n");
} */

return part[sum/2]
;
}

/*Driver function to check for above function*/
public static void main (String[] args)
{
int arr[] = {3, 1, 1, 2, 2,1};
int n = arr.length;
if (findPartition(arr, n) == true)
System.out.println("Can be divided into two "
"subsets of equal sum");
else
System.out.println("Can not be divided into"
" two subsets of equal sum");

}
}
/* This code is contributed by Devesh Agrawal */


输出:

Can be divided into two subsets of equal sum


下图显示了值所在的分区表,这个图来自wiki page of partition problem



时间复杂度是:O(sum*n)

空间复杂度是:O(sum*n)

注意:如果数组的元素的和太大的话,这个方法就不再适用了。

参考文献:

http://en.wikipedia.org/wiki/Partition_problem
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: