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,是最小的。
输入
第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数
输出
输出最小正子段和。
输入样例
8
4
-1
5
-2
-1
2
6
-2
输出样例
1
思路:
虽然说遇到连续和会想到前缀和,但是这道题对前缀和的使用实在是太巧妙了! Orz大佬的思想:
我们记录前缀和的位置pos,以及前缀和的值val
然后对前缀和的值进行排序
然后我们找相邻的点,如果满足前面的pos小于后面的pos,它们 val 的差大于0 那么我们就记录它们的val之差
为什么是找相邻的点就可以了呢?
解释一下为什么只需检查相邻2个数就可以,设ABC是排序后的结果,如果A同B不能组成序列,而A同C可以组成序列,那么B同C也可以组成序列,并且BC会是一个更优的解。
略微证明下:
因为A和B不能组成序列,那么B的pos 小于 A的pos。 因为A和C可以组成序列,那么C的pos 大于 A的pos
所以B和C肯定也可以组成序列!因为又是排序过了的,所以B和C的差值肯定是优于A和C的差值的
具体代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <stdlib.h> 4 #include <string> 5 #include <string.h> 6 #include <set> 7 #include <queue> 8 #include <stdbool.h> 9 10 #define LL long long 11 #define inf 0x3f3f3f3f 12 using namespace std; 13 const int MAXN=50005; 14 15 typedef struct Node{ 16 LL val; 17 int pos; 18 }Node; 19 20 Node a[MAXN] = {0}; 21 22 bool cmp(Node a,Node b) 23 { 24 return a.val < b.val; 25 } 26 27 28 int main() 29 { 30 #ifndef ONLINE_JUDGE 31 freopen("../in.txt","r",stdin); 32 #endif 33 int n,flag; 34 LL temp; 35 LL sum = 0; 36 scanf("%d",&n); 37 a[0].val = 0; 38 a[0].pos = 0; 39 for (int i=1;i<=n;i++) 40 { 41 cin >> temp; 42 sum += temp; 43 a[i].val = sum; 44 a[i].pos = i; 45 } 46 sort(a,a+1+n,cmp); 47 LL res; 48 flag = 0; 49 for (int i=1;i<=n;i++) 50 { 51 if (a[i].pos-a[i-1].pos>0 && a[i].val-a[i-1].val>0) 52 { 53 if (flag == 0) 54 { 55 res = a[i].val-a[i-1].val; 56 flag = 1; 57 } 58 else 59 { 60 if ( a[i].val-a[i-1].val<res) 61 res = a[i].val-a[i-1].val; 62 } 63 } 64 } 65 cout << res << endl; 66 return 0; 67 }