小阳买水果(前缀和,单调栈,思维)

链接:https://ac.nowcoder.com/acm/contest/949/D
来源:牛客网

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

输入描述:

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

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

输出描述:

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

示例1

输入

5
0 0 -7 -6 1

输出

1

备注:
1 ≤ n ≤ 2 × 1 e 6 1≤n≤2×1e 6 1n2×1e6, ∣ a i ∣ ≤ 1 e 3 ∣a_ i ∣≤1e 3 ai1e3

代码:

way1:

#include 
using namespace std;
const int MAXN = 2e6+5;
#define INF 0x3f3f3f3f
int sum[MAXN];
int s[MAXN];
int main()
{
     int n;
     scanf("%d",&n);
     int x;
     sum[0] = 0;
     for(int i = 1; i <= n; i ++)
     {
         scanf("%d",&x);
         sum[i] = sum[i-1] + x;
     }
     
     //way1:,sum数组存前缀和,s数组存i到最后的后缀最大和
     memset(s,-INF,sizeof(s));
     for(int i = n; i  > 0; i--)
     {
         s[i] = max(sum[i],s[i+1]);
     }
     int ans = 0;
     for(int i  = 0,j = 1; i < n; i++,j++)
     {
         if(sum[i]>=s[j]) //move at the same time,because ans that in the middle  less than recent ans
             continue;
         else
         {
             while(s[j]>sum[i]&&j<=n)  j++;
             j--;  // the last greater than sum[i]
         }
         ans = max(ans,j-i);
     }
     printf("%d\n",ans);
    return 0;
}

way2:

#include 
using namespace std;
const int MAXN = 2e6+5;
#define INF 0x3f3f3f3f
int sum[MAXN];
int s[MAXN];
int main()
{
     int n;
     scanf("%d",&n);
     int x;
     sum[0] = 0;
     for(int i = 1; i <= n; i ++)
     {
         scanf("%d",&x);
         sum[i] = sum[i-1] + x;
     }
     //way2:s数组存前面到到i的前缀最小和
     s[0] = INF;
     for(int i = 1; i <= n; i++)
     {
         s[i] = min(s[i-1],sum[i]);
     }
     int ans = 0;
     for(int i = 1; i <= n; i++)
     {
         if(sum[i] > 0)
         {
             ans = max(ans,i);
         }
         else if(s[i-1] >= sum[i]) continue;
         else
         {
             //the first less than sum[i] in range(1,i-1);
             int l = 1,r = i-1;
             int pos;
             while(l <= r)
             {
                 int mid = (l+r)>>1;
                 if(s[mid] < sum[i])
                 {
                     pos = mid;
                     r = mid - 1;
                 }
                 else
                 {
                     l = mid + 1;
                 }
             }
             ans = max(ans,i-pos);
         }
     }
     printf("%d\n",ans);
    return 0;
}

way3:

直接排序,按前缀和递增排序,
前缀和相同所在位置大的(pos)排序
(ps: 前缀和相同时,pos小的在前面结果可能会更新,pos大的前面就能保证结果不被更新),
排序前要记录原来的位置(pos)
为什么直接排序可以做呢?
基本思路是,排完序后,
后面的前缀和一定比前面的大,
只要前面有个 原来的index(pos)
比当前位置的 原来index(pos)小就可以更新答案

#include 
using namespace std;
const int MAXN = 2e6+5;
struct Node
{
    int sum;
    int pos;
    bool operator<(const Node &a)const
    {
        if(sum == a.sum)
            return pos > a.pos;
        return sum < a.sum;
    }
};
Node a[MAXN];
int main()
{
    int n;
    scanf("%d",&n);
    a[0].pos = 0;
    a[0].sum = 0;
    int x;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&x);
        a[i].pos = i;
        a[i].sum = a[i-1].sum + x;
    }
    sort(a,a+n+1);
     /*
    the worst situation maybe TLE,
    because the quickSort the worst situation need O(n^2)
    */
    int minx = 1<<30,ans = 0;
    for(int i = 0; i <= n; i++)//此时已有单调性(单调递增)
    {
        minx = min(minx,a[i].pos);
        if(a[i].pos > minx)
            ans = max(ans,a[i].pos-minx);
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(思维题)