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(n)的求解。
我们用一个结构体,里面有两个成员变量,val表示sum[i](即前缀和),dex表示sum[i]在前缀和序列中的下标。
将sum[i]的val按从小打到排序,如果sum[i]有相同的情况,则按下标从小到大排序。
在一遍遍历中,我们保证在sum[i].val>sum[i-1].val && sum[i].dex>sum[i-1].dex 的前提下,不断更新最小正字段和,即ans = min(ans,sum[i].val-sum[i-1].val)
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50005;
int n;
long long a;
struct Node
{
long long val;//后面long long 会卡精度,要注意
int dex;
};
Node sum[maxn];
bool cmp(Node x,Node y)
{
if(x.val!=y.val)
return x.val<y.val;
else
return x.dex<y.dex;
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif // LOCAL
ios_base::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
cin>>n;
cin>>a;
sum[1].val = a;
sum[1].dex = 1;
for(int i=2;i<=n;i++)
{
cin>>a;
sum[i].val = sum[i-1].val+a;
sum[i].dex = i;
}
int ans = 0x3f3f3f3f;
sort(sum+1,sum+n+1,cmp);//"前缀和" 和 "贪心"
for(int i=1;i<=n;i++)
{
if(sum[i].val>0 && sum[i].val<ans)
ans = sum[i].val;
if(sum[i].val>sum[i-1].val && sum[i].dex>sum[i-1].dex && sum[i].val-sum[i-1].val<ans)
{
ans = sum[i].val-sum[i-1].val;
}
}
cout<<ans<<endl;
return 0;
}