【Leetcode 每日一题】1013. 将数组分成和相等的三个部分

题目描述

给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。

形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + ... + A[i] == A[i+1] + A[i+2] + ... + A[j-1] == A[j] + A[j-1] + ... + A[A.length - 1]) 就可以将数组三等分。

示例 1:

输出:[0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1

示例 2:

输入:[0,2,1,-6,6,7,9,-1,2,0,1]
输出:false

示例 3:

输入:[3,3,6,5,-2,2,5,1,-9,4]
输出:true
解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4

提示:

  • 3 <= A.length <= 50000
  • -10^4 <= A[i] <= 10^4

思路——寻找切分点

观察测试用例,将数组每个元素更新为它与前面所有项的和,那么和相等的三个区间一定遵循Sum,2*Sum,3*Sum规则。如果能够拆分为三个和相同的区间,只需要依次找到中间分割的两个索引即可。

  • [0,2,1,-6,6,-7,9,1,2,0,1]——用例1
  • [0,2,3,-3,3,-4,5,6,8,8,9]——加和

9是3*Sum,依次存在3,6,返回true。

  • [0,2,1,-6,6,7,9,-1,2,0,1]——用例2
  • [0,2,3,-3,3,10,19,18,20,20,21]——加和

21是3*Sum,无7,14,返回false。

  • [3,3,  6, 5,  -2,  2,  5,  1, -9, 4]——用例3
  • [3,6,12,17,15,17,22,23,14,18]——加和

18是3*Sum,依次存在6,12,返回true。

下面是第一次完全通过提交的代码,提交过程中遇到的问题有:

  1. 仅考虑了能够分割为3个和相等的区间,未考虑第三个区间结尾必须在数组尾部。
  2. 寻找第二个划分点时,未注意索引范围限制,必须留有第三个区间至少一个元素

最后提交虽然通过了,可再次提交发现最后一个用例变成了数组有50000个元素,我未作优化超时了,因此查看官方题解与其他大佬的代码作优化。

class Solution {
public:
    bool canThreePartsEqualSum(vector<int>& A) {
        for(int i = 0; i < A.size() - 1; i++)
            A[i + 1] += A[i];
        for(int i=0;i<A.size();i++)
        {
            //i is first index,look for second index
            int j=i+1;
            while(j<A.size() && 2*A[i]!=A[j]) j++;
            if(j>=A.size()-1) continue;//若是最后一个元素,则第三区间不存在了,错误
            
            //check for last sum
            if(3*A[i]==A[A.size()-1])
                return true;
        }
        return false;
    }
};

思路——官方题解(优化)

  1. 我们将数组 A 中的所有数的和记为 sum(A),每一个非空部分的和都应当是 sum(A) / 3。因此,sum(A)不能整除3时直接返回false。
  2. 知道了sum(A) / 3我们只需遍历一次数组,查找等于  sum(A)/3 与 sum(A)/3 * 2 的索引。
  3. 根据竞赛大佬的经验,static auto x = []()可以提高输入输出的速度,果然用时从60ms提高到了32ms,神奇。具体原因可参考关于 static auto x = []()的用法解析
//输入输出速度优化
static const auto _ = []()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return nullptr;
}();

优化的完整代码

static const auto _ = []()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return nullptr;
}();
class Solution {
public:
    bool canThreePartsEqualSum(vector<int>& A) {
        int i;
        //逐项求前i项和
        for(i = 0; i < A.size() - 1; i++)
            A[i + 1] += A[i];
        //满足题解的数组和一定是3的倍数
        if(A[i]%3 != 0) return false;

        int target = A[i]/3;
        //查找第一个区间分割点
        int j=0;
        while(j<A.size()-2)
        {
            if(A[j] == target)
                break;
            j++;
        }
        if(A[j] != target) return false;

        //查找第二个区间分割点
        int k=j+1;
        while(k<A.size()-1)
        {
            if(A[k] == 2 * target)
                return true;
            k++;
        }

        return false;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的内容,我们可以使用状态压缩法来枚举所有可能的分组情况。对于给定的数组,我们可以将每个元素放入数组1或数组2中,用二进制的1和0来表示。然后,我们可以计算每种分组情况下两个数组的和之差的绝对值,并找到最小的差值。 举个例子,对于输入数组nums = \[2,-1,0,4,-2,-9\],我们可以将其分成 \[2,4,-9\] 和 \[-1,0,-2\] 两个数组,它们的和之差的绝对值为 abs((2 + 4 + -9) - (-1 + 0 + -2)) = 0。这就是最优的分组方案。 因此,我们可以通过枚举所有可能的分组情况,并计算每种情况下的差值,找到最小的差值来实现将数组分成两个数组,并最小化数组和的差。 #### 引用[.reference_title] - *1* [2035. 将数组分成两个数组并最小化数组和的差 折半搜索](https://blog.csdn.net/yu_duan_hun/article/details/125899854)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [[Leetcode]5897. 将数组分成两个数组并最小化数组和的差](https://blog.csdn.net/gshgsh1228/article/details/120692058)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值