最小正子段和

N个整数组成的序列a1,a2,a3,…,an,从中选出一个子段(ai,ai+1,…aj

),使这个子段的和>0,并且这个和是所有和>0的子段中最小的。

例如:4,-1,5,-2,-1,2,6,-2。-1,5,-2,-1,序列和为1,是最小的。

Input第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数Output输出最小正子段和。Sample Input
8
4
-1
5
-2
-1
2
6
-2

首先处理出前缀和数组。在前缀和数组中,我们要求的是dmin=sum[i]-sum[j](当 i>j && sum[i]-sum[j]>0 时)。
那么就可以用一个新的结构体来存储每个sum[i]和它的编号。即 b[i].id=i; b[i].w=sum[i];
此时sort之后我们就有了一个按照 前缀和大小 排序的带有顺序的结构体。
之后再用一个循环去挨个遍历找相邻连个的最下差值。

注意当 b[i].w==b[j].w 的时候,我们要让 id 大的在前面。这样的话,当有很多前缀和都相等的时候,比如 2 3 3 3 3 3 3 4,就避免了许多不必要的比较。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
const int MAXN = 50000+3;
using namespace std;
typedef long long ll;

int n,Min; ll a[MAXN],sum[MAXN],ans;

struct B{int id;ll w;}b[MAXN];

bool Com(B x,B y){
    if(x.w==y.w) return (x.id>y.id);
    return (x.w<y.w);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i].w=sum[i]=sum[i-1]+a[i];
        b[i].id=i;
    }
    
    sort(b+1,b+1+n,Com);
    if(b[1].w>0) ans=b[1].w;//注意不要忽略以a[1]开头的一段区间!!!
    else ans=0x3f3f3f3f3f3f3f3f;
    
    for(int i=2;i<=n;i++){
        int d=b[i].w-b[i-1].w;
        if(b[i].w>0 && ans>b[i].w) ans=b[i].w;//注意不要忽略以a[1]开头的一段区间!!!
        if(b[i].id>b[i-1].id && d>0 && ans>d){
            ans=(ll)b[i].w-b[i-1].w;
        }
    }
    printf("%lld",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Siegfried-L/p/13086791.html