【动态规划】【数学】【C++算法】805 数组的均值分割

简介: 【动态规划】【数学】【C++算法】805 数组的均值分割

作者推荐

视频算法专题

本文涉及知识点

动态规划汇总

数学

805 数组的均值分割

给定你一个整数数组 nums

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

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

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

示例 1:

输入: nums = [1,2,3,4,5,6,7,8]

输出: true

解释: 我们可以将数组分割为 [1,4,5,8] 和 [2,3,6,7], 他们的平均值都是4.5。

示例 2:

输入: nums = [3,1]

输出: false

参数范围:

1 <= nums.length <= 30

0 <= nums[i] <= 104

动态规划

令n=nums.length half=n/2。不失一般性,令A的长度小于等于B,则lena的长度取值范围[1,half],lenb=n-lena。

数组A的和为:Sumi = 0 : n − 1 \Large_{i=0}^{:n-1}i=0:n1nums[i]*lena/n ,数组A的和必须是整数。nums[i]的和最大为104*30 ,lean最大为15,可以直接相乘,看对n的余数是否为0。如果超出整数范围,可以先对lena和n约分。

预处理CountToTotal

将nums分成两部分。vLeft[i]记录所有从nums[0,half)选择i个数 可能的和。 vRight[i]记录所有从nums[half,n)中选择i个数的和。下面以vLeft为例,vRightei类似。三层循环

第一层循环:枚举[0,half)。时间复杂度O(n)

第二层循环:从大到小枚举vLeft[j]。 时间复杂度(n)。

第三层循环:枚举vLeft[j-1]。 时间复杂度O(2^n)

动态规划的转移方程:本轮的vLeft[j]一定是上一轮的vLeft[j-1]+nums[i]。总时间复杂度O(1515215) 约106

处理

第一层循环:用变量i枚举lena。

第二层循环:枚举A从左边选择了j个元素,j的取值范围[0,i]。

第三层循环:通过iLeft枚举vLeft[j],看vRight[i-j]是否存在totalA - iLeft。

时间复杂度: O(nn2n)

代码

核心代码

int GCD(int n1, int n2)
{
  int t1 = min(n1, n2);
  int t2 = max(n1, n2);
  if (0 == t1)
  {
    return t2;
  }
  return GCD(t2 % t1, t1);
}
class Solution {
public:
  bool splitArraySameAverage(vector<int>& nums) {
    const int n = nums.size();
    const int half = n / 2;
    vector<unordered_set<int>> vLeft(1), vRight(1);   
    CountToTotal( vLeft, nums.data(),0, half);
    CountToTotal(vRight, nums.data()+half, 0, n-half);
    const int total = std::accumulate(nums.begin(), nums.end(),0);
    for (int i = 1; i <= half; i++)
    {
      const int iGCD = GCD(i, n);
      if (0 != total % (n / iGCD))
      {
        continue;//A的和必定为整数
      }
      const int totalA = total * i / n;
      for (int j = 0; j <= i; j++)
      {// A数组总共选择i个,其中从左边选择j个
        for (const auto& iLeft : vLeft[j])
        {
          if (vRight[i - j].count(totalA - iLeft))
          {
            return true;
          }
        }
      }
    }
    return false;
  }
  void CountToTotal(std::vector<std::unordered_set<int>>& v, const int* p,int left,int r)
  {
    v[0].emplace(0);
    for (int i = left; i < r ; i++)
    {
      v.emplace_back();
      for (int j = v.size() - 1; j >= 1; j--)
      {
        for (const auto& pre : v[j - 1])
        {
          v[j].emplace(pre + p[i]);
        }
      }
    }
  }
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  vector<int> nums;
  {
    Solution sln;
    nums = { 1,2,3,4,5,6,7,8 };
    auto res = sln.splitArraySameAverage(nums);
    Assert(true, res);
  }
  {
    Solution sln;
    nums = { 3,1};
    auto res = sln.splitArraySameAverage(nums);
    Assert(false, res);
  }
  {
    Solution sln;
    nums = { 18,10,5,3 };
    auto res = sln.splitArraySameAverage(nums);
    Assert(false, res);
  }
  
}

2023年1月

class Solution {

public:

bool splitArraySameAverage(vector& nums) {

if (1 == nums.size())

{

return false;

}

const int iLeftMaxLen = nums.size() / 2;

const int iRightMaxLen = nums.size() - iLeftMaxLen;

vector<std::unordered_set> vLeftLenSums(iLeftMaxLen+1),vRightLenSums(iRightMaxLen+1);

vLeftLenSums[0].insert(0);

for (int i = 0; i < iLeftMaxLen; i++)

{

for (int j = i ; j >=0 ; j-- )

{

for (auto& it : vLeftLenSums[j])

{

vLeftLenSums[j + 1].insert(it +nums[i]);

}

}

vLeftLenSums[1].insert(nums[i]);

}

vRightLenSums[0].insert(0);

for (int i = iLeftMaxLen; i < nums.size(); i++)

{

for (int j = i - iLeftMaxLen; j >= 0; j–)

{

for (auto& it : vRightLenSums[j])

{

vRightLenSums[j + 1].insert(it + nums[i]);

}

}

}

int iTotalSum = std::accumulate(nums.begin(), nums.end(), 0);

for (int iALen = 1; iALen + 1 <= nums.size(); iALen++)

{

double dSum = iTotalSum 1.0 / nums.size() iALen;

int iSum = dSum;

bool bInt = ((dSum - iSum) < 0.0001) || ((dSum - iSum) > 0.9999);

if (!bInt)

{

continue;

}

for (int iLeftLen = max(0,iALen-iRightMaxLen); (iLeftLen <= min(iALen, iLeftMaxLen)); iLeftLen++)

{

for (const auto& it : vLeftLenSums[iLeftLen])

{

if (vRightLenSums[iALen - iLeftLen].count(iSum - it))

{

return true;

}

}

}

}

return false;

}

};


相关文章
|
1月前
|
算法 测试技术 C++
【动态规划算法】蓝桥杯填充问题(C/C++)
【动态规划算法】蓝桥杯填充问题(C/C++)
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
31 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
478 0
高精度算法(加、减、乘、除,使用c++实现)
|
1月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
21 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
1月前
|
存储 算法 决策智能
【算法】博弈论(C/C++)
【算法】博弈论(C/C++)
|
1月前
|
存储 算法 C++
【算法】哈希映射(C/C++)
【算法】哈希映射(C/C++)
|
1月前
|
机器学习/深度学习 人工智能 算法
【算法】最长公共子序列(C/C++)
【算法】最长公共子序列(C/C++)
|
1月前
|
人工智能 算法 BI
一篇带你速通差分算法(C/C++)
一篇带你速通差分算法(C/C++)
|
1月前
|
人工智能 算法 C++
一篇带你速通前缀和算法(C/C++)
一篇带你速通前缀和算法(C/C++)
|
1月前
|
存储 算法 C++
弗洛伊德(Floyd)算法(C/C++)
弗洛伊德(Floyd)算法(C/C++)
下一篇
无影云桌面