In a given integer array A, we must move every element of A to either list B or list C. (B and C initially start empty.)

Return true if and only if after such a move, it is possible that the average value of B is equal to the average value of C, and B and C are both non-empty.

Example :
Input:
[1,2,3,4,5,6,7,8]
Output: true
Explanation: We can split the array into [1,4,5,8] and [2,3,6,7], and both of them have the average of 4.5.

Note:

  • The length of A will be in the range [1, 30].
  • A[i] will be in the range of [0, 10000].

给一个数组A,把A中的每一个元素都移到数组B或C中(B, C初始为空)。如果移动后可以使B和C的均值相等,则返回ture。其实就是将一个数组分成两部分,每部分的平均值相同。

题目的关键点是:当能拆分成两个平均值相等的数组时,拆分的数组和原来数组的平均值是相同的。因此问题转换为,先算出A的平均值A_aver,如果A中的数组成的子数组B的平均值等于A_aver,再去判断剩余的数组成的数组C的平均值是否和A_aver相等。

The key thing of this problem is, when we are able to make a same average split, the average of each splitted array is the same as the average of the whole array.
So the problem can be transformed into a simpler one: given a target number (tosum), can we construct it using a specific number (lenB) of integers in a list(A).
Then we can try every possible numbers of lenB, to see whether any one is feasible.

If the array of size n can be splitted into group A and B with same mean, assuming A is the smaller group, then

totalSum/n = Asum/k = Bsum/(n-k), where k = A.size() and 1 <= k <= n/2;
Asum = totalSum*k/n, which is an integer. So we have totalSum*k%n == 0;

如果一个长度为n的数组可以被划分为A和B两个数组,假设A的长度小于B并且A的大小是k,那么:total_sum / n == A_sum / k == B_sum / (n - k),其中1 <= k <= n / 2。可得出:A_sum = total_sum * k / n。由于A_sum一定是个整数,所以可以推导出total_sum * k % n == 0,那就是说,对于特定的total_sum和n而言,符合条件的k不会太多。首先验证是否存在符合条件的k,如果不存在就可以提前返回false。

解法1: early pruning + knapsack DP, O(n^3 * M)

如果经过第一步的验证,发现确实有符合条件的k,那么我们在第二步中,就试图产生k个子元素的所有组合,并且计算他们的和。这里的思路就有点类似于背包问题了,我们的做法是:定义vector<vector<unordered_set<int>>> sums,其中sums[i][j]表示A[0, i]这个子数组中的任意j个元素的所有可能和。可以得到递推公式是:sums[i][j] = sums[i - 1][j] "join" (sums[i][j - 1] + A[i]),其中等式右边的第一项表示这j个元素中不包含A[i],而第二项表示这j个元素包含A[i]。这样就可以采用动态规划的思路得到sums[n - 1][k]了(1 <= k <= n / 2)。

有了sums[n - 1][k],我们就检查sums[n - 1][k]中是否包含(total_sum * k / n)。一旦发现符合条件的k,就返回true,否则就返回false。

If there are still some k valid after early pruning by checking totalSum*k%n == 0,
we can generate all possible combination sum of k numbers from the array using DP, like knapsack problem. (Note: 1 <= k <= n/2)
Next, for each valid k, simply check whether the group sum, i.e. totalSum * k / n, exists in the kth combination sum hashset.

vector<vector<unordered_set<int>>> sums(n, vector<unordered_set<int>>(n/2+1));
sums[i][j] is all possible combination sum of j numbers from the subarray A[0, i];
Goal: sums[n-1][k], for all k in range [1, n/2]
Initial condition: sums[i][0] = {0}, 0 <= i <= n-1; sums[0][1] = {all numbers in the array};
Deduction: sums[i+1][j] = sums[i][j] "join" (sums[i][j-1] + A[i+1])
The following code uses less space but the same DP formula.
Runtime analysis:
All numbers in the array are in range [0, 10000]. Let M = 10000.
So the size of kth combination sum hashset, i.e. sums[...][k], is <= k * M;
For each number in the array, the code need loop through all combination sum hashsets, so
the total runtime is n * (1 * M + 2 * M + ... + (n/2) * M) = O(n^3 * M)

解法2: TLE, For such k, the problem transforms to "Find k sum = Asum, i.e. totalSum * k/n, from an array of size n". This subproblem is similar to LC39combination sum, which can be solved by backtracking.

Python:

class Solution(object):
def splitArraySameAverage(self, A):
if len(A)==1: return False
global_avg = sum(A)/float(len(A))
for lenB in range(1, len(A)/2+1):
if int(lenB*global_avg) == lenB*global_avg:
if self.exist(lenB*global_avg, lenB, A):
return True
return False def exist(self, tosum, item_count, arr):
if item_count==0:
return False if tosum else True
if item_count > len(arr) or not arr:
return False
if any([self.exist(tosum-arr[0], item_count-1, arr[1:]),
self.exist(tosum, item_count, arr[1:])]):
return True
return False 

Python:

# Time:  O(n^4)
# Space: O(n^3)
class Solution(object):
def splitArraySameAverage(self, A):
"""
:type A: List[int]
:rtype: bool
"""
def possible(total, n):
for i in xrange(1, n//2+1):
if total*i%n == 0:
return True
return False
n, s = len(A), sum(A)
if not possible(n, s):
return False sums = [set() for _ in xrange(n//2+1)];
sums[0].add(0)
for num in A: # O(n) times
for i in reversed(xrange(1, n//2+1)): # O(n) times
for prev in sums[i-1]: # O(1) + O(2) + ... O(n/2) = O(n^2) times
sums[i].add(prev+num)
for i in xrange(1, n//2+1):
if s*i%n == 0 and s*i//n in sums[i]:
return True
return False  

C++: 1

class Solution {
public:
bool splitArraySameAverage(vector<int>& A) {
int n = A.size(), m = n/2, totalSum = accumulate(A.begin(), A.end(), 0);
// early pruning
bool isPossible = false;
for (int i = 1; i <= m && !isPossible; ++i)
if (totalSum*i%n == 0) isPossible = true;
if (!isPossible) return false;
// DP like knapsack
vector<unordered_set<int>> sums(m+1);
sums[0].insert(0);
for (int num: A) {
for (int i = m; i >= 1; --i)
for (const int t: sums[i-1])
sums[i].insert(t + num);
}
for (int i = 1; i <= m; ++i)
if (totalSum*i%n == 0 && sums[i].find(totalSum*i/n) != sums[i].end()) return true;
return false;
}
};  

C++: 1

class Solution {
public:
bool splitArraySameAverage(vector<int>& A) {
int n = A.size(), m = n / 2;
int totalSum = accumulate(A.begin(), A.end(), 0);
// early pruning
bool isPossible = false;
for (int i = 1; i <= m; ++i) {
if (totalSum * i % n == 0) {
isPossible = true;
break;
}
}
if (!isPossible) {
return false;
}
// DP like knapsack
vector<unordered_set<int>> sums(m + 1);
sums[0].insert(0);
for (int num: A) { // for each element in A, we try to add it to sums[i] by joining sums[i - 1]
for (int i = m; i >= 1; --i) {
for (const int t: sums[i - 1]) {
sums[i].insert(t + num);
}
}
}
for (int i = 1; i <= m; ++i) {
if (totalSum * i % n == 0 && sums[i].find(totalSum * i / n) != sums[i].end()) {
return true;
}
}
return false;
}
};  

C++: 2 TLE

class Solution {
public:
bool splitArraySameAverage(vector<int>& A) {
int n = A.size(), m = n/2, totalSum = accumulate(A.begin(), A.end(), 0);
sort(A.rbegin(), A.rend()); // Optimization
for (int i = 1; i <= m; ++i)
if (totalSum*i%n == 0 && combinationSum(A, 0, i, totalSum*i/n)) return true;
return false;
}
bool combinationSum(vector<int>& nums, int idx, int k, int tar) {
if (tar > k * nums[idx]) return false; // Optimization, A is sorted from large to small
if (k == 0) return tar == 0;
for (int i = idx; i <= nums.size()-k; ++i)
if (nums[i] <= tar && combinationSum(nums, i+1, k-1, tar-nums[i])) return true;
return false;
}
};

  

All LeetCode Questions List 题目汇总

[LeetCode] 805. Split Array With Same Average 用相同均值拆分数组的更多相关文章

  1. 805. Split Array With Same Average

    In a given integer array A, we must move every element of A to either list B or list C. (B and C ini ...

  2. [LeetCode] Split Array With Same Average 分割数组成相同平均值的小数组

    In a given integer array A, we must move every element of A to either list B or list C. (B and C ini ...

  3. [Swift]LeetCode805. 数组的均值分割 | Split Array With Same Average

    In a given integer array A, we must move every element of A to either list B or list C. (B and C ini ...

  4. [LeetCode] 659. Split Array into Consecutive Subsequences 将数组分割成连续子序列

    You are given an integer array sorted in ascending order (may contain duplicates), you need to split ...

  5. [LeetCode] 410. Split Array Largest Sum 分割数组的最大值

    Given an array which consists of non-negative integers and an integer m, you can split the array int ...

  6. [LeetCode] 548. Split Array with Equal Sum 分割数组成和相同的子数组

    Given an array with n integers, you need to find if there are triplets (i, j, k) which satisfies fol ...

  7. LeetCode 548. Split Array with Equal Sum (分割数组使得子数组的和都相同)$

    Given an array with n integers, you need to find if there are triplets (i, j, k) which satisfies fol ...

  8. leetcode 659. Split Array into Consecutive Subsequences

    You are given an integer array sorted in ascending order (may contain duplicates), you need to split ...

  9. LeetCode 842. Split Array into Fibonacci Sequence

    原题链接在这里:https://leetcode.com/problems/split-array-into-fibonacci-sequence/ 题目: Given a string S of d ...

随机推荐

  1. C# 退出应用程序的几种方法

    Application.Exit();//好像只在主线程可以起作用,而且当有线程,或是阻塞方法的情况下,很容易失灵 this.Close();//只是关闭当前窗体. Application.ExitT ...

  2. JS获取访客IP进行自动跳转

    因业务需要进行地区判断跳转指定站点,下面是我个人实现的办法,分享给大家,仅供参考,切勿做非法用途 第一步,获取IP并判断归属地 直接使用搜狐的IP库查询接口 <script type=" ...

  3. sqlserver2005新特性介绍

    1.更强的编程能力-CLR集成 增强了数据库的编程能力,将一些逻辑层(Bll)转移到数据库中,减少了网络中的数据流量,但是增加了服务器cpu的负荷,当我们需要操作大量的数据,但是产生很少的数据,把这种 ...

  4. Hadoop跨集群迁移数据(整理版)

    1. 什么是DistCp DistCp(分布式拷贝)是用于大规模集群内部和集群之间拷贝的工具.它使用Map/Reduce实现文件分发,错误处理和恢复,以及报告生成.它把文件和目录的列表作为map任务的 ...

  5. python获取参数列表

    def f(a=1, b=2, c=3): print(locals())#在函数内获取 #使用inspect模块,简单方便 python2.7: import inspectinspect.geta ...

  6. 当调用对象中不存的方法、属性时,__getattr__的应用场景

    一.Python中创建类和实例的调用顺序 new(cls) 创建对象前调用,如果类中没定义,会一直向父类找,直到object的 new 方法创建类.cls代表类本身 init(self) 创建类实例后 ...

  7. Codeforces 1251D Salary Changing

    D. Salary Changing 大意: 有n个变量, 每个变量有一个取值区间, 要求给这n个变量赋值, 使得n个变量的和不超过S且中位数尽量大(n一定为奇数) 二分答案, 中位数大于等于mid就 ...

  8. oracle 按每天,每周,每月,每季度,每年查询统计数据

    oracle 按每天,每周,每月,每季度,每年查询统计数据 //按天统计 select count(dataid) as 每天操作数量, sum() from tablename group by t ...

  9. tensorflow2.0 学习(三)

    用tensorflow2.0 版回顾了一下mnist的学习 代码如下,感觉这个版本下的mnist学习更简洁,更方便 关于tensorflow的基础知识,这里就不更新了,用到什么就到网上取搜索相关的知识 ...

  10. ROM

    ROM 是 read only memory的简称,表示只读存储器,是一种半导体存储器.只读存储器(ROM)是一种在正常工作时其存储的数据固定不变,其中的数据只能读出,不能写入,即使断电也能够保留数据 ...