【51nod 1065】【贪心+前缀和】最小正子段和【最小正子串和】

来源:互联网 发布:java web 七牛云接口 编辑:程序博客网 时间:2024/04/30 12:45

传送门:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1065

思路:

可以参见夹克老爷的回复的那个帖子:http://bbs.csdn.net/topics/370106383。

对于这类连续序列的问题可以先求出前缀和,对于每个位置求某个位置到当前位置和大于1的和的最小值。然而这是复杂度是O(n^2)的。

其实可以通过排序优化到O(nlogn)

具体做法是先对这些前缀和排序。
如果相近的位置上的node满足其pos的前后关系,就比较最小值,因为是相近的,所以已经是最小值候选了,其余的绝对不可能了


具体为什么采用求相邻两个数的贪心策略,这引用夹克老爷的话:解释一下为什么只需检查相邻2个数就可以,设ABC是排序后的结果,如果A同B不能组成序列,而A同C可以组成序列,那么B同C也可以组成序列,并且BC会是一个更优的解(A与B不能形成队列,说明posA>posB,A与C能形成队列,说明posA<posC,那就一定有posB<posC,而BC排序后相邻,所以BC的差值肯定不大于AC的差值)。


代码:

#include <bits/stdc++.h>using  namespace  std;#define ll __int64const int N=5e4+10;struct node{    ll val;    int pos;    bool operator < (const node& tmp)const    {        if(val == tmp.val)return pos>tmp.pos;        return val<tmp.val;    }}p[N];int  main(){    int n, mx;    scanf("%d", &n);    p[0].val=0;    p[0].pos=0;    for(int i=1; i<=n; i++){        scanf("%lld", &p[i].val);        p[i].val += p[i-1].val;        p[i].pos=i;    }    sort(p, p+n+1);      ll ans=0;    for(int i=1; i<=n; i++){        if(p[i].pos>p[i-1].pos && p[i].val>p[i-1].val){            if(ans == 0)ans = p[i].val - p[i-1].val;            else ans = min(ans, p[i].val - p[i-1].val);        }    }    printf("%lld\n", ans);    return 0;}


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示例
84-15-2-126-2
Output示例
1
相关问题
循环数组最大子段和 
10
 
最大子段和 
0
 
最大子段和 V2 
160
 
最大M子段和 V3 
320
最大M子段和 V2 
160
 
最大M子段和 
80
 
最大子矩阵和 
40
李陶冶 (题目提供者)


0 0
原创粉丝点击