最小正子段和(前缀和+思维)
最小正子段和
基准时间限制: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
分析:最近打牛客网的练习赛碰到一个和这个有点关系的题目,只能想到前缀和,后面就没了思路,队友告诉我和这个有点像,所以决定写一下,觉得写过的很多类似的题;
N的取值范围为2 <= N <= 50000,所以暴力肯定会超时,所以我们用前缀和!但不是一般的前缀和,我们定义一个结构体,一个变量用来存储前缀和,一个变量用来存储下标,按前缀和排序,后面的比较就是个思维了!仔细想想,排序后,一个数字只有和它相邻数的差或者它本身为最小值,代码还是很好写的!
AC代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 50050
typedef long long ll;
using namespace std;
struct point
{
ll w;
int v;
}b[N];
int a[N];
bool cmp(point x,point y)
{
return x.w<y.w;
}
int main()
{
int n;
cin>>n;
for (int i=1;i<=n;i++)
cin>>a[i];
b[1].w=a[1];
for (int i=2;i<=n;i++)
{
b[i].w=b[i-1].w+a[i];
b[i].v=i;
}
sort(b+1,b+1+n,cmp);
ll ans;
if (b[1].w>0) ans=b[1].w;
else ans=99999999;
for (int i=2;i<=n;i++)
{
if (b[i].w>0&&b[i].w<ans)
ans=b[i].w;
if (b[i].w>b[i-1].w&&b[i].v>b[i-1].v&&(b[i].w-b[i-1].w<ans))
ans=b[i].w-b[i-1].w;
}
cout << ans << endl;
return 0;
}