博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动态规划之分区问题(Partition problem)
阅读量:4099 次
发布时间:2019-05-25

本文共 4714 字,大约阅读时间需要 15 分钟。

原文地址:

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

例子:

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 problemimport 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 problemimport 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][n];    }    /*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

下图显示了值所在的分区表,这个图来自

这里写图片描述

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

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

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

参考文献:

你可能感兴趣的文章
DirectX11 点光
查看>>
DirectX11 聚光灯
查看>>
DirectX11 HLSL打包(packing)格式和“pad”变量的必要性
查看>>
DirectX11 光照演示示例Demo
查看>>
VUe+webpack构建单页router应用(一)
查看>>
Vue+webpack构建单页router应用(二)
查看>>
从头开始讲Node.js——异步与事件驱动
查看>>
Node.js-模块和包
查看>>
NodeJS开发指南——mongoDB、Session
查看>>
Express: Can’t set headers after they are sent.
查看>>
2017年,这一次我们不聊技术
查看>>
实现接口创建线程
查看>>
HTML5的表单验证实例
查看>>
程序设计方法概述:从面相对象到面向功能到面向对象
查看>>
JavaScript基础1:JavaScript 错误 - Throw、Try 和 Catch
查看>>
SQL基础总结——20150730
查看>>
SQL join
查看>>
JavaScript实现页面无刷新让时间走动
查看>>
CSS实例:Tab选项卡效果
查看>>
前端设计之特效表单
查看>>