51Nod 1065-最小正子段和

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40727946/article/details/81386359

1065 最小正子段和 

基准时间限制: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;
} 

猜你喜欢

转载自blog.csdn.net/qq_40727946/article/details/81386359
今日推荐