最小正子段和
基准时间限制:1 秒 空间限制:131072 KB
N个整数组成的序列a[1],a[2],a[3],…,a[n],从中选出一个子序列(a[i],a[i+1],…a[j]),使这个子序列的和>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
输出最小正子段和。
Input示例
8 4 -1 5 -2 -1 2 6 -2
Output示例
1
思路:先求出前缀和,然后对前缀和进行排序,排序后求出最大的相邻前缀和之差,并且要满足:相邻前缀和在排序前的位置是递增的。输出这个答案即可
简单地考虑一下这个算法的正确性:如果没有进行排序,求前后两个前缀和的差的最大值是没有错的,暴力计算需要O(n2)的复杂度,
排序后,设此时的前缀和为sum[i],每个前缀和对应排序前的位置为p[i],相邻的前缀和如果满足更新条件,即sum[p[i]]>sum[p[i-1]]&&p[i]>p[i-1],
假设存在一个x<i-1也满足相同条件,我们令p[i]>p[x]>p[i-1]且sum[p[i]]-sum[p[x]]<sum[p[i]]-sum[p[x]],
即存在与第i个前缀和不相邻的前缀和sum[p[x]],第i个前缀和与第x个前缀和的差值要优于与第i-1的差值,那么就有sum[p[x]]>sum[p[i-1]],但是x<i-1,和排序结果是矛盾的,
所以不存在x获得排序后第i个前缀和的最优解,即最优解一定是排序后相邻前缀和的差值。
AC代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 typedef long long LL; 7 const int MAXN=5e4+10; 8 const LL INF=1e15+10; 9 LL sum[MAXN]; 10 int p[MAXN]; 11 bool cmp(int a, int b){ 12 return sum[a]<sum[b]; 13 } 14 int main() 15 { 16 int n,m; 17 sum[0]=0; 18 scanf("%d", &n); 19 for(int i=0;i<=n;i++) p[i]=i; 20 for(int i=1;i<=n;i++){ 21 scanf("%d", &m); 22 sum[i]=sum[i-1]+m; 23 } 24 sort(p, p+n+1, cmp); 25 LL res=INF; 26 for(int i=1;i<=n;i++){ 27 if(sum[p[i]]-sum[p[i-1]]>0&&p[i]>p[i-1]) 28 res=min(res, sum[p[i]]-sum[p[i-1]]); 29 } 30 printf("%lld ", res); 31 }