【leetcode】【2022/11/14】805. 数组的均值分割
admin
2024-01-22 11:34:40
0次

问题描述:

  • 给定你一个整数数组 nums,我们要将 nums 数组中的每个元素移动到 A 数组或者 B 数组中,使得 A 数组和 B 数组不为空,并且 average(A) == average(B)。
    • 如果可以完成则返回 true,否则返回 false。
    • 满足 1 <= nums.length <= 30。

核心思路:

  • 该题较难,考察了数学推导 + 位运算 + 折半搜索。
  • 数学推导部分如下:
    • 首先经过推导可得知,数组 A 或数组 B 的平均值和原数组 nums 的平均值是相等的。
    • 因此将数组中所有数值减去 sum(nums)/n,原问题就可以转换为找到一个数组 A 其求和为 0。【其中 n 为数组长度】
    • 而直接求 nums 的平均值可能会得到浮点值影响最后结果的精度,所以在求平均值前先将 nums 中所有数值乘以数组的长度,再将所有数值减去 sum(nums),仍然可以将问题转化为找到一个数组 A 其求和为 0。
    • 也就是说,对每一个数值 num 进行 num * n - sum(nums) 的预处理,最后找到 nums 中哪些数求和为 0 即可。
  • 而想要计算出所有的子集求和情况,就需要用位运算来维护元素的存取情况:
    • 因为 1 <= n <= 30,所以所有子集的存取有 2n2^{n}2n 种情况,用第 i 位用来判断数组第 i 个元素是否存入数组 A。
    • 因此我们遍历 [1, 1 << n) 即遍历所有存取情况;在每一个子集中遍历数组元素,来获得当前子集求和情况。
    • 但如此计算就是暴力求解,时间复杂度为 O(2n×n)\mathcal{O}(2^n\times n)O(2n×n),当 n = 30 时自然会超时。
  • 解决的办法就是折半搜索:【该思想是本题解的主要难点】
    • 顾名思义就是搜索前半段和后半段,时间复杂度就会缩减为 O(2n2×n)\mathcal{O}(2^{\frac{n}{2}}\times n)O(22n​×n)。
    • 但此时需要进行讨论,最后子集 A 可能出现三种情况:
      1. 子集元素全部存在于数组前半段;【此时搜索前半段就可以得到某个子集求和为 0】
      2. 子集元素全部存在于数组后半段;【此时搜索后半段就可以得到某个子集求和为 0】
      3. 子集元素存在于前半段和后半段。
    • 情况 3 较为复杂,此时就需要用空间换时间,利用哈希表来保存前半段的所有子集和,在后半段的遍历中利用哈希表中的值即可。
    • 用哈希表来保存值时,还可能出现前半段和后半段元素同时全选的情况,此时需要避免判断该情况,具体看代码注释即可。
  • 该题的动态规划是根据 sum(nums) 的范围推导的,也就是根据值域大小来获得所有子集求和的情况,官方题解给出的动态规划解法时间复杂度为 O(n2×sum(nums))\mathcal{O}(n^2\times sum(nums))O(n2×sum(nums)),一旦求和的范围过大,动态规划解法就可能会超时。

代码实现:

  • 折半搜索解法代码实现如下:
    class Solution {
    public:bool splitArraySameAverage(vector& nums) {int n = nums.size(); // n 的取值范围为 [1, 30]if(n == 1) return false;int sum = accumulate(nums.begin(), nums.end(), 0);for(int& num : nums) num = num * n - sum; // 原本是 num = num - sum/n,但这样写会有精度问题int m = n >> 1; // 折半计算unordered_set ss;for(int i = 1; i < (1 << m); ++i) { // 二进制表示选择结果int t = 0;for(int j = 0; j < m; ++j)if(i & (1 << j)) t += nums[j]; // 求和子数组if(t == 0) return true;ss.insert(t);}for(int i = 1; i < 1 << (n-m); ++i) {int t = 0;for (int j = 0; j < (n-m); ++j)if(i & (1 << j)) t += nums[m+j];if(t == 0 or (i != (1 << (n-m))-1 and ss.count(-t))) return true;// 注意 i != (1 << (n-m))-1 表示不能“同时”左子数组全选和右子数组全选,因为同时全选就是整个数组全部选择,则必定返回 true// 左子数组全选的情况已经存在 ss 中,所以遍历右子数组之后,判断完右子数组全选不为 0 之后,就需要避免左右子数组同时全选的情况}return false;}
    };
    

参考内容:

  • 折半查找 + 二进制枚举(详解)

相关内容

热门资讯

蜻蜓订购APP贵金属订购非法期...   蜻蜓订购APP以"投资平台"名义对外宣传,主打贵金属期货(订购)交易。该平台一方面强调自身"正规...
悟空订购APP非法期货交易欺骗...   近期,有投资者反映,“悟空订购APP”等平台以白银、贵金属订购为名,宣传所谓“高收益”“低门槛”...
小赢淘金APP期货投资骗局,平...   “小赢淘金”之流,最擅长的就是用“注册送现金”、“充值返券”的糖衣炮弹来引流。别天真地以为那是福...
八方淘金APP非法期货交易欺骗...   警惕!八方生财、每日淘金、小赢淘金、悟空订购、蜻蜓订购、斗金订购等APP均涉嫌非法期货交易!  ...
智易购APP贵金属订购交易骗局...   很多人看到“3元起投”“充值送福利”“操作简单”“随时提现”这类宣传,很容易把智易购APP当成普...
喜玛银庄APP商品订购投资骗局...   喜玛银庄APP白银、铂金“现货订购”骗局警示:无资质平台涉嫌非法期货交易,投资者爆仓后如何维权?...
吉鑫乐享APP炒贵金属订购交易...   近期,多名投资者反映在“吉鑫乐享APP”进行贵金属投资时遭遇严重亏损,甚至血本无归。看似正规的“...
易掘金APP虚假期货投资骗局,...   “易掘金”的APP它打着“高收益、灵活投资”的旗号,宣称通过“商品订购”就能轻松赚钱,吸引了不少...
在大吉订购APP订购交易亏损被...   近期,不少投资者反映在“大吉订购App”上投资白银、铂金等商品遭遇严重亏损。该平台宣称提供现货订...
铂银壹号APP商品订购交易亏损...   许多中小投资者误以为网上的“商品现货订购”是合规合法的理财渠道,殊不知这背后隐藏着巨大的合规风险...
兴鑫淘金APP贵金属订购交易亏...   白银、贵金属或其他商品类投资,除国家批准的正规期货交易场所及持牌期货公司外,市面上大量第三方交易...
铂银掘金APP无资质的违规平台...   铂银掘金等平台的投资者,往往是被“注册送现金”“充值返现券”“首单免亏”等广告话术吸引,进而下载...
八方淘金APP订购交易不合规,...   “八方淘金APP”“铂银掘金APP”“铂银壹号APP”等软件被指以“现货订购”为名,实际采用高杠...
在指尖订购APP订购商品亏损被...   指尖订购APP这类平台,表面是“订购交易”,实则是披着电商外衣的非法期货陷阱。绝大多数投资者被蒙...
在犀贝易购APP做商品订购交易...   市面上打着“现货投资”“贵金属交易”旗号的APP并不少,但真正合规、受监管的平台并不多。犀贝易购...
易达淘金APP虚假商品订购交易...   易达淘金APP存在严重的虚假宣传与违规交易行为。该平台打着“商品订购”的旗号,实则通过不合规的交...
大浪淘金APP白银订购亏损被骗...   大浪淘金APP最隐蔽的套路,在于其完全封闭的内盘交易系统。表面上,它宣称与国际贵金属市场接轨;实...
一品好购APP上订购白银出现亏...   一品好购APP原名“一品订购”,运营方为上海宝智未来信息技术有限公司。近期不少投资者反映在该平台...
九州订购APP贵金属订购投资骗...   九州订购APP正通过抖.音、快手、今日头条等平台大量投放投资广告,用“5元起投”“充值返现”“0...
坚锅淘金APP交易软件并不正规...   当前,市面上大量所谓的“现货商城订购”交易软件,早已偏离了正常的实物商品买卖,演变为高杠杆、高手...