最长连续和大于0(小阳买水果)

博客围绕小阳买水果问题展开,即求给定数组中最长连续和大于0的片段长度。介绍了尺取和线性两种解题方法,指出尺取法代码难写,线性方法较简单,还对排序规则及加入空值的原因进行了解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述 

水果店里有 n个水果排成一列。店长要求顾客只能买一段连续的水果。
小阳对每个水果都有一个喜爱程度 ai,最终的满意度为他买到的水果的喜欢程度之和。
如果和为正(不管是正多少,只要大于 0 即可),他就满意了。

小阳想知道在他满意的条件下最多能买多少个水果。

你能帮帮他吗?

输入描述:

第一行输入一个正整数 n,表示水果总数。

第二行输入 n 个整数 ai,表示小阳对每个水果的喜爱程度。

输出描述:

一行一个整数表示结果。(如果 1 个水果都买不了,请输出 0)

输入

复制

5
0 0 -7 -6 1

输出

复制

1

备注:

1≤n≤2×10^6,|ai|≤10^3

链接:https://ac.nowcoder.com/acm/contest/949/D

解题思路:题意是在一个给定的数组中,求最长的连续和大于0,比如2,-5,2,0,-1,最长连续和大于0的片段是:2,0,-1,所以结果ans=3。既然是连续片段的问题,解题方法肯定要偏向尺取、线性。

如果是尺取方法,就会很麻烦,面临的问题就是右边界向右移一个值,在保证该片段和大于0情况下,求当前最长片段,就要左边界就要左移和右移,并将片段和、长度记录在有边界数下,思路很简单,但是代码不好写。

线性方法就很简单,先用结构体记录前缀和和当前位置,然后把前缀和从小到大sort,如果前缀和num相同按下标从大到小排序(这点很重要!),sort之前在结构体中再加一个空值下表=0,前缀和=0,将n+1个数组排序后,数组中的前缀和num必然是越来越大,所以数组后面num减数组前面num一定大于等于0,同时还要保证数组后面的下标pos大于前面的下标pos,因为只有在pos大于情况下,后面前缀和num才包括前面前缀和。在前后关系中,要想得到最长片段,必然前面pos越小,后面pos越大,所以最长max_ans=后面pos-前面pos

-4,5,1,-2,1,-3,1
前缀和num-4-2-100112
下表pos16740523

问题1:相同前缀和num下,下标pos从大到小排序?

比如num=0,pos排序为4和0,因为后num-前num=0,与题意不符合,这时候刚pos 0<pos 4,所以在计算ans时,不会算入。如果顺序是0和4,ans就会等于4-0=4。但是在上述数据中,并没有明显体现出来。

问题2:数组中加入空值num=0,pos=0,进行sort ?

想想如果不加时,pos最大为n,最小为1,max_ans=n-1,但是最长是ans=n,此时pos最大n,最小为0,才可以算出n=n-0。num=0和pos=0也算是分界点,num>0的部分 ,前缀片段都是从头开始计算,当前maxpos=pos-0,都可以进行正确计算。

 

#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
    int num,pos;  // 记录前缀和num,下标为pos
}a[2000010];      // a[pos]=num;
bool cmp(node x,node y)
{
    if(x.num==y.num)
    return x.pos>y.pos;
    return x.num<y.num;
}
void solve()
{
	int n;
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        a[i].num=a[i-1].num+x; //记录前缀和
        a[i].pos=i;            //当前下标pos
    }
    a[0].pos=0;
    a[0].num=0;			//加入一个num=0标志位 
    sort(a,a+n+1,cmp);      //(n+1)个前缀和从小到大排序
    int minpos=n,ans=0;
    for(int i=0;i<=n;i++)
    {
        minpos=min(minpos,a[i].pos);  //0到i个前缀和中,最小的下标
        if(minpos<a[i].pos)          
        ans=max(ans,a[i].pos-minpos); 
        //a[i].num-a[minpos].num>0 所以最长长度=当前前缀下标-最小的下标 
    }
    printf("%d\n",ans);
} 
int main()
{
    solve(); 
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值