枚举+优化(8)——前缀和2

例3.题目链接:hihoCoder1534

 这道题要注意一下数据范围。首先N小于等于10万。其次Ai,也就是数组中每个数的值,是在负100万到正100万之间。假如这里Ai都是正整数的话,那么总共的划分方法不会太多,因为随着p增大,第一段的和S1是不断增大的。  但是因为这里Ai有可能是0,也有可能是负数,所以划分方法可能非常多。例如有可能数据是10万个Ai全都是0。这样随便一个划分都是满足条件的。答案是C(99999, 2),甚至超过了int范围。  我们看一下样例,A数组是[3, 2, 1, 0, 2]。一共有3种切分方法:第一种是3|2|1 0 2,3单独一组,2单独一组,然后1 0 2一组,三段的和依次是3,2,3;第二种是3|2 1|0 2|,3单独一组,2和1一组,0 2一组,三段的和依次是3,3, 2;第三种是3|2 1 0|2,3单独一组,2 1 0一组,0单独一组,三段的和依次是3 3 2。

思路

 这道题一个基本的思路就是枚举3段的断点p和q,然后求出来3段的和S1, S2和S3。然后判断S1,S2和S3是不是满足题目要求,也就是相差不超过1。如果满足条件就给答案累加1。因为这里S1,S2和S3都是部分和,所以可能用前缀和来计算。但是在此之前,我们需要一个函数来判断三段和是否满足条件,如果满足条件就让结果+1。伪代码如下:

Ok(S1,S2,S3):
If |S1 - S2| <= 1 and |S1 - S3| <= 1 and |S2 - S3| <= 1
    return true
return false

 然后我们预处理前缀和,以及进行枚举。伪代码如下:

//预处理前缀和
S[0] = 0
For i = 1...N
    S[i] = S[i - 1] + A[i]
//枚举+判断
Ans = 0
For p = 1...N-2
    For q = p+1...N-1
        S1 = S[p]

 上面这个伪代码预处理前缀和的复杂度是O(N)。后面枚举pq的复杂度是O(N^2^)。对于确定的pq,判断三段和是不是满足条件是O(1)。所以整体复杂度是O(N^2)。至此,题目中70%的分数应该能拿到了,还有30%需要优化

优化

 优化的方法当然还是从枚举入手,我们假设只枚举q,也就是最后一段的断点。我们来分析一下这时候我们知道哪些信息,距离我们要的答案还有多远。

 看上图,假设A数组是[3, 2, 1, 0, 2],然后我们枚举q=N-1,也就是第三段只包含A[N]=2这一个数。在q已经确定的情况下,可以知道S3的值。比如上面的例子中S3=2。根据题目要求S1、S2和S3的差不能超过1。所以对于一个合法的切分方案,S1的取值只可能是S3-1, S3, S3+1三种,也就是1,2,3  但是由于S1+S2+S3的和是整个数组的和,也就是8。所以S1的三种取值不见得都能成立。比如S1=1这种情况,由于S3=2是确定的,所以S2一定等于8-1-2=5。这是S2与S3相差超过1,不符合题目要求。所以S1=1这种情况不成立。同理S1=2也是不成立的。但是S1=3是成立的,因为这时S2的值是8-3-2=3。S2与S1和S3相差都不超过1  在S[1], S[2]和S[3]三个前缀和中,有几个的值是3。因为每有一个前缀和的值是3,都等价于找到了一种满足条件的切分方案:我们把这个前缀作为第一段,它的和等于3;剩下的就是第二段,它的和一定是3

 实际例子中我们可以看上图,绿色的数组是前缀和。S[1], S[2], S[3]分别是3,5,6,只有S[1]=3。所以q=N-1这种情况下,只有一种切分方案:3|2 1 0|2  上面是q=N-1的情况,我们再枚举q=N-2的情况,如下图:

 S3还是等于2。S1的可能取值还是S3-1,S3,S3+1,也就是1,2,3。其中只有S1=3时,S2的值满足题目要求。所以我们的问题变成S[0]和S[1]中,有几个前缀和等于3?

 因为前缀和S[1]=3, S[2]=5,所以还是只有S[1]=3一个解。于是q=N-2这种情况下,也只有一种切分方案:3|2 1|0 2  最后一种情况是q=N-3的时候:

 这时S3值是3。于是S1的取值范围是S3-1, S3, S3+1,也就是2, 3, 4。S1=2是成立的,因为这时S2=8-2-3=3,{2, 3, 3}相差都不超过1。S1=3也是成立的,因为这时S2=8-3-3=2,{3, 2, 3}相差都不超过1。S1=4是不成立的。于是我们的问题变成,前缀和S[0]中,有几个前缀和的值是2,有几个前缀和的值是3

 这时只有S[1]=3一个前缀和满足条件,所以q=N-3时,只有一组划分方法:3|2|1 0 2  上面我们就把样例的3种划分方法都求出来了。我们回顾一下上面的思路,基本就是从N-1到2枚举q,对于每一个q,我们都要求一类问题的解:  q = N-1时,前缀和S[1], S[2], … S[N-2]中有几个前缀和的值是X?(这里X可能是S3-1, S3, S3+1)  q = N-2时,前缀和S[1], S[2], … S[N-3]中有几个前缀和的值是X?(这里X可能是S3-1, S3, S3+1) …  q = 2时,前缀和S[1]中有几个前缀和的值是X?(这里X可能是S3-1, S3, S3+1)  当然上面枚举q的过程中S3的值会变化,另外注意我们关心的前缀和集合也在变短。  对于这一类问题,就是一堆前缀和中,有几个X,我们当然可以用哈希表来实现。使得每次询问的复杂度都是O(1)的。具体来说,我们可以用unordered_map<long long, int> cnt来保存每个值出现了几次,例如cnt[3]表示前缀和是3的有几个。注意这里key的类型是long long,因为前缀和可能超过int范围

#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
int n,a[100001];
long long s[100001],ans = 0;
unordered_map<long long,int> cnt;

bool ok(long long s1,long long s2,long long s3)
{
    if(abs(s1 - s2) <= 1 && abs(s1 - s3) <= 1 && abs(s2 - s3) <= 1)
        return true;
    return false;
}

int main()
{
    cin >> n;
    s[0] = 0;
    for(int i = 1;i <= n;i++)
    {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
        if(i < n)
            cnt[s[i]]++;
    }
    long long s3 = 0;
    for(int q = n - 1;q >= 2;q--)
    {
        s3 += a[q + 1];
        cnt[s[q]]--;
        for(long long s1 = s3 - 1;s1 <= s3 + 1;s1++)
        {
            long long s2 = s[n] - s1 - s3;
            if(ok(s1,s2,s3))
                ans += cnt[s1];
        } 
    }
    cout << ans;
    return 0;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏雪胖纸的玩蛇日常

老男孩Python全栈开发(92天全)视频教程 自学笔记14

1382
来自专栏技术最杂谈

map容器clear操作不会释放内存?

当第一次听到这个说法的时候确实有点惊讶。一直记得map容器底层红黑树会自动析构节点,并释放内存。在同事进行了代码验证,并百度了答案后,我也变得不确定起来了。

2686
来自专栏xingoo, 一个梦想做发明家的程序员

堆 栈-相关知识

一、预备知识—程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量...

1738
来自专栏用户画像

7.7.1外部排序

内部排序都是在内存中进行的,而在实际应用中,经常需要对大文件进行排序,因为文件中的记录很多、信号量庞大,无法将整个文件拷贝进内存中进行排序。因此,需要将待排序的...

551
来自专栏玄魂工作室

Python数据结构与算法-在M个数中找K个最小的数

比如输入10,-9,0,100,90,1,4,-9;找到最小的3个数为:-9,-9,0

951
来自专栏CSDN技术头条

基于Zookeeper的分布式锁

实现分布式锁目前有三种流行方案,分别为基于数据库、Redis、Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开。我们来看下使用Zo...

2248
来自专栏北京马哥教育

Linux 中断处理浅析

最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着”重要的事马上做, 不重要的事推后做”的异步处理思想. 于是整理一下~ ? 第...

4188
来自专栏云霄雨霁

Java虚拟机--Java堆中对象的创建和布局

1594
来自专栏互联网大杂烩

小米面试经历

他是看了我写了一篇这样的博客才问的,可惜我都忘了自己写了啥!吃亏了,博客太久了,都忘记看了。链接如下: http://blog.csdn.net/zpdrea...

762
来自专栏Python小屋

Python内置函数iter()语法及应用

iter()函数用来返回指定对象的迭代器,有两种用法:iter(iterable)和iter(callable, sentinel),前者要求参数必须为序列或者...

3526

扫码关注云+社区