基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题
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
题解:
先求出前缀和和,对于每个位置求某个位置到当前位置和大于0的和的最小值。然而这是复杂度是O(n^2)的。其实可以通过排序优化到O(nlogn)。对前缀和排序,然后比较相邻两项其位置关系,如果可以组成序列(即排序后的第几项前缀和的大小顺序是 前小后大),则说明其可能是所要求结果,然后从所有可能是结果的结果中取出最小值即可。
解释一下为什么只需检查相邻2个数就可以,设ABC是排序后的结果,如果A同B不能组成序列,而A同C可以组成序列,那么B同C也可以组成序列,并且BC会是一个更优的解。
AC代码:
#include<bits/stdc++.h>
typedef long long int ll;
using namespace std;
struct node
{
ll sum; //注意这里加法可能会溢出int的范围
int b;
}aum[50000+100];
int cmp(node x,node y)
{
if(x.sum==y.sum) //考虑和相等的情况
return x.b>y.b; //从大到小逆序,只要最多的项满足,则其他项和一定满足。重!
return x.sum<y.sum;
}
int main()
{
ll n;
cin>>n;
aum[0].sum=0; //把0也加入其中,即与最小的前缀和比较。
aum[0].b=0;
for(int i=1;i<=n;i++)
{
ll a;
cin>>a;
aum[i].sum=aum[i-1].sum+a; //计算前缀和
aum[i].b=i;
}
sort(aum,aum+1+n,cmp); //排序的时候有 n+1 项 注意!
ll Min=100000000;
for(int i=1;i<=n;i++)
{
if(aum[i].b>aum[i-1].b&&aum[i].sum>aum[i-1].sum)
{
if((aum[i].sum-aum[i-1].sum)<Min)
{
Min=aum[i].sum-aum[i-1].sum;
}
if(aum[i].sum>0&&aum[i].sum<Min)
{
Min=aum[i].sum;
}
}
}
cout<<Min<<endl;
return 0;
}