动态规划问题(十三)是否能够划分为总和相等的子集

动态规划问题(十三)是否能够划分为总和相等的子集

问题描述

​ 给你一个数组,你的任务是检测是否能过够将这个数组分成两个总和相等的子集。

​ 例如,对于数组 {1, 5, 11, 5},它能过够分为两个子数组 {1, 5, 1} 和 {11}。对于{1, 5, 3}数组则不能分为两个总和相等的子数组。

解决思路

​ 首先,一个数组能否分为符合要求的两个数组,整个数组的总和应该是一个偶数,如果是一个奇数的话,那么无论如何这个数组都不能分为两个数组。

​ 之后,将这个数组和的一般作为目标和,枚举所有的可能组合检测是否能够得到最终的结果。

  • 递归方案

    • 对于当前的元素,它要么是在当前要组合的子数组中的,要么不是,因此:

      \[result(arr, n, target) = result(arr, n - 1, target) || result(arr, n - 1, target - arr[n-1]) \]

      其中,n表示当前的添加元素的位置索引

    • 边界情况:对于 target = 0 的情况,说明可以得到子数组;对于 n < 0 的情况,说明已经完成了所有的检测,但是无法组成子数组。

  • 动态规划

    • 使用自底向上的方式,通过记录中间的计算结果,可以极大地提高运算速度。

实现

  • 递归

    public class Solution {
        public static boolean findPartition(int[] arr) {
            int sum = 0;
            for (int v : arr) sum += v;
    
            if ((sum & 1) != 0) // 检测原数组总和是否能够被分为两部分
                return false;
    
            return isSubsetSum(arr, arr.length - 1, sum / 2);
        }
        
        public static boolean isSubsetSum(int[] arr, int n, int target) {
            if (target == 0) return true; 
            if (n < 0) return false;
    
            if (arr[n] > target) // 如果当前元素大于目标值,那么不能放入当前的子数组
                return isSubsetSum(arr, n - 1, target);
    
            return isSubsetSum(arr, n - 1, target) // 当前元素没有放入子数组
                || isSubsetSum(arr, n - 1, target - arr[n]); // 当前元素放入了数组
        }
    }
    
  • 动态规划

    public class Solution {
        public static boolean findPartitionDp(int[] arr) {
            int sum = 0;
            for (int v : arr) sum += v;
    
            if ((sum & 1) != 0)
                return false;
    
            int len = arr.length;
            int target = sum / 2;
            boolean[][] dp = new boolean[len + 1][target + 1];
    
            for (int i = 0; i <= len; ++i)
                dp[i][0] = true; // 边界情况,对于目标值为 0 的情况,不管数组内的元素是什么,总能找到对应的子集
    
            for (int i = 1; i <= len; ++i) {
                for (int j = 1; j <= target; ++j) {
                    dp[i][j] = dp[i - 1][j]; // 当前元素未放入检出子数组中的情况
                    if (arr[i - 1] <= j) {
                        dp[i][j] = dp[i][j] || dp[i - 1][j - arr[i - 1]]; // 与上文递归的公式对应
                    }
                }
            }
    
            return dp[len][target];
        }
    }
    
posted @ 2021-08-20 13:36  FatalFlower  阅读(128)  评论(0编辑  收藏  举报