【每日一题Day27】LC805数组的均值分割 | 01背包

数组的均值分割【LC805】

给定你一个整数数组 nums

我们要将 nums 数组中的每个元素移动到 A 数组 或者 B 数组中,使得 A 数组和 B 数组不为空,并且 average(A) == average(B)

如果可以完成则返回true , 否则返回 false

**注意:**对于数组 arr , average(arr)arr 的所有元素除以 arr 长度的和。

You are given an integer array nums.

You should move each element of nums into one of the two arrays A and B such that A and B are non-empty, and average(A) == average(B).

Return true if it is possible to achieve that and false otherwise.

Note that for an array arr, average(arr) is the sum of all the elements of arr over the length of arr.

做了好久做出来了,很有很有成就感

  • 思路:转化为01背包问题
    • 当能够分成两个等均值的数组时,均值为固定值,即为数组nums的均值AVE,推导如下,nA和nB分别代表数组A和数组B的长度,sum为数组nums的和,a=average(A) = average(B)

      • nA * a + nB * a = sum
      • a = sum/(nA+nB)=AVE
    • 因此如果能够找到任意n个数的和为均值AVE的n倍时,即可分割成两个等均值的数组

    • 每个元素只有取或者不取两种可能性,因此可以转化为01背包问题

  • 01背包问题实现
    • 背包的体积为sum
    • 背包要放入的商品(集合里的元素)重量为元素的个数(每个元素为1),价值为 元素的数值
    • 背包中每一个元素是不可重复放入。
    • 要求:找到一个背包的容量为nA,存满时其存在价值大小为AVE*nA,sumA = sum/n * nA
      • 因为存在浮点数,所以将等式改为sumA * n = sum * nA

二维动态规划

  1. 确定dp数组(dp table)以及下标的含义

    dp[i][j]表示 表示是否能从 nums 中选 i个数使它们的和为 j

    背包总容量为j时,能否装满背包所需的元素个数为i

  2. 确定递推公式

    • 放元素nums[k]

      当当前遍历元素为nums[k]时,如果dp[-1][j] = true,那么dp[i] [j+nums[k]]= true

    • 不放元素nums[k]:dp[i][j] = dp[i][j]

  3. dp数组如何初始化

    dp[0][0] = true;
    
  4. 确定遍历顺序

    按顺序遍历每个物品,先逆序遍历背包容量i【避免重复放入同一个物品】,再正序遍历背包价值j

  5. 举例推导dp数组

  • 代码

    class Solution {
        public boolean splitArraySameAverage(int[] nums) {
            int len = nums.length;
            if (len <= 1){
                return false;
            }
            int sum = 0;
            for (int i = 0; i < len; i++){
                sum += nums[i];
            }
            boolean[][] dp = new boolean[len + 1][sum + 1];
            dp[0][0] = true;
            for (int k = 0; k < len ; k++){
                for (int i = k + 1; i >= 1; i--){
                    for (int j = 0; j <= sum; j++){
                        if (dp[i-1][j] && j + nums[k] <= sum){
                            dp[i][j+nums[k]] = true;
                            if ( i != 0 && i != len && (j + nums[k]) * len  == sum * i){
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }
    }
    
    • 复杂度

      • 时间复杂度: O ( n 2 ∗ s u m ) O(n^2*sum) O(n2sum),n为数组长度,sum为数组和
      • 空间复杂度: O ( n ∗ s u m ) O(n*sum) O(nsum),dp数组大小
  • 将二维数组转换为HashSet数组,set[n]代表当选取n个元素时,和可能的值

    class Solution {
        public boolean splitArraySameAverage(int[] nums) {
            int len = nums.length;
            if (len <= 1){
                return false;
            }
            int sum = 0;
            for (int i = 0; i < len; i++){
                sum += nums[i];
            }
            Set<Integer>[] dp = new Set[len + 1];
            for (int i = 0; i <= len; i++) {
                dp[i] = new HashSet<Integer>();
            }
            dp[0].add(0);
            for (int k = 0; k < len ; k++){
                for (int i = k + 1; i >= 1; i--){
                    for (int x : dp[i-1]){
                        int curSum = x + nums[k];
                        if (i != 0 && i != len && curSum * len == sum * i){
                            return true;
                        }
                        dp[i].add(curSum);
                    }
                }
            }
            return false;
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值