链接:https://ac.nowcoder.com/acm/contest/949/D
来源:牛客网
水果店里有 n个水果排成一列。店长要求顾客只能买一段连续的水果。
小阳对每个水果都有一个喜爱程度 aii,最终的满意度为他买到的水果的喜欢程度之和。
如果和为正(不管是正多少,只要大于 0 即可),他就满意了。
小阳对每个水果都有一个喜爱程度 aii,最终的满意度为他买到的水果的喜欢程度之和。
如果和为正(不管是正多少,只要大于 0 即可),他就满意了。
小阳想知道在他满意的条件下最多能买多少个水果。
你能帮帮他吗?输入描述:
第一行输入一个正整数 n,表示水果总数。
第二行输入 n 个整数 ai,表示小阳对每个水果的喜爱程度。
输出描述:
一行一个整数表示结果。(如果 1 个水果都买不了,请输出 0)
备注:
1≤n≤2×1e6,∣ai∣≤1e3
两种做法:
O(n)
就是前缀和排个序,求一下前缀和,并且记录每个前缀和的位置,然后把 前缀和从小 到大排序,前缀和相同按照位置,从大到小排序。
区间和等于两个前缀和相减。排序之后前缀和小的在前大的 在后,只需从前 往后遍历一遍,记录最小位置即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e6+5; struct node{ ll a; int p; }a[maxn]; bool cmp(node a,node b){ if(a.a==b.a) return a.p>b.p; else return a.a<b.a; } int main(){ a[0].a=0,a[0].p=0; int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lld",&a[i].a); a[i].a+=a[i-1].a; a[i].p=i; } sort(a,a+n+1,cmp); int p=a[0].p; int ans=0; for(int i=1;i<=n;i++){ if(a[i].p>p){ ans=max(ans,a[i].p-p); }else{ p=a[i].p; } } printf("%d ",ans); return 0; }
首先一个数组a记录权值,然后数组sum保存a的前缀和,数组psum表示从后往前的sum 最大值(即max后缀,易知psum是 单调递减 的),然后枚举起点l(1-n),每次循环令r=n,pos,通过 二分 找到 最大的pos使得满足psum[mid]-sum[l-1]>0(满足题意),然后此时的长度就为pos-i+1,每次循环取max即可~
dalao原话:利用前缀和的后缀max的单调性,它是单调递减的,sum[i]表示前缀和,pmax[i]表示从i~n中最大的sum[j],i<=j<=n。然后枚举起点i,在[i,n]上二分终点,只要pmax[mid]>sum[i-1]就表示满足条件,继续增加l=mid+1,否则不满足条件令r=mid-1
#include<bits/stdc++.h> using namespace std; const int maxn=2e6+5; const int mod=1e9+7; typedef long long ll; int n,ans; int a[maxn],sum[maxn],psum[maxn]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",a+i); sum[i]=sum[i-1]+a[i]; } psum[n]=sum[n]; for(int i=n-1;i>=1;i--) psum[i]=max(psum[i+1],sum[i]); for(int i=1;i<=n;i++){ int l=i,r=n,pos=0; while(l<=r){ int mid=(l+r)>>1; if(psum[mid]>sum[i-1]){ l=mid+1; pos=mid; } else r=mid-1; } ans=max(pos-i+1,ans); } cout<<ans<<endl; }