【动态规划】【数学】【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 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;
}
};


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关文章
|
4天前
|
机器学习/深度学习 算法
扩散模型=进化算法!生物学大佬用数学揭示本质
在机器学习与生物学交叉领域,Tufts和Harvard大学研究人员揭示了扩散模型与进化算法的深刻联系。研究表明,扩散模型本质上是一种进化算法,通过逐步去噪生成数据点,类似于进化中的变异和选择机制。这一发现不仅在理论上具有重要意义,还提出了扩散进化方法,能够高效识别多解、处理高维复杂参数空间,并显著减少计算步骤,为图像生成、视频合成及神经网络优化等应用带来广泛潜力。论文地址:https://arxiv.org/pdf/2410.02543。
31 21
|
25天前
|
机器学习/深度学习 人工智能 算法
Transformer打破三十年数学猜想!Meta研究者用AI给出反例,算法杀手攻克数学难题
《PatternBoost: Constructions in Mathematics with a Little Help from AI》提出了一种结合传统搜索算法和Transformer神经网络的PatternBoost算法,通过局部搜索和全局优化交替进行,成功应用于组合数学问题。该算法在图论中的Ramsey数研究中找到了更小的反例,推翻了一个30年的猜想,展示了AI在数学研究中的巨大潜力,但也面临可解释性和通用性的挑战。论文地址:https://arxiv.org/abs/2411.00566
69 13
|
1月前
|
机器学习/深度学习 算法 PyTorch
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
软演员-评论家算法(Soft Actor-Critic, SAC)是深度强化学习领域的重要进展,基于最大熵框架优化策略,在探索与利用之间实现动态平衡。SAC通过双Q网络设计和自适应温度参数,提升了训练稳定性和样本效率。本文详细解析了SAC的数学原理、网络架构及PyTorch实现,涵盖演员网络的动作采样与对数概率计算、评论家网络的Q值估计及其损失函数,并介绍了完整的SAC智能体实现流程。SAC在连续动作空间中表现出色,具有高样本效率和稳定的训练过程,适合实际应用场景。
207 7
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
|
1月前
|
负载均衡 算法 安全
探秘:基于 C++ 的局域网电脑控制软件自适应指令分发算法
在现代企业信息化架构中,局域网电脑控制软件如同“指挥官”,通过自适应指令分发算法动态调整指令发送节奏与数据量,确保不同性能的终端设备高效运行。基于C++语言,利用套接字实现稳定连接和线程同步管理,结合实时状态反馈,优化指令分发策略,提升整体管控效率,保障网络稳定,助力数字化办公。
52 19
|
27天前
|
算法 Java C++
【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题
本文介绍了经典的0/1背包问题及其动态规划解法。
46 5
|
1月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
49 2
|
2月前
|
存储 算法 安全
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
|
1月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
2月前
|
算法 安全 C++
用 C++ 算法控制员工上网的软件,关键逻辑是啥?来深度解读下
在企业信息化管理中,控制员工上网的软件成为保障网络秩序与提升办公效率的关键工具。该软件基于C++语言,融合红黑树、令牌桶和滑动窗口等算法,实现网址精准过滤、流量均衡分配及异常连接监测。通过高效的数据结构与算法设计,确保企业网络资源优化配置与安全防护升级,同时尊重员工权益,助力企业数字化发展。
65 4
|
3月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
81 2