四、1065 最小正子段和

1065 最小正子段和

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

解题思路:首先利用前缀和可以表示出所有的子串,通过for循环也可以遍历每一个子串,找出最小的正子串。

基本思路有了剩下的就是优化,通过对于前缀和的优化,,两种方式线段树,和树状数组,这个题显然树状数组比较合适,通过树状数组优化也降低不了整体的时间复杂度,毕竟还有两个for循环,N^2的复杂度,这里就要用到一个选择的方法了,我叫他SORT筛法。

sort筛法:通过sort排序将所有的字串排序,然后只考虑相邻的可以形成子串的进行选择。

sort筛选依据:通过排序之后形成ABC的形式,如果A不能和B形成字串,A和C可以形成,那么B和C也一定能形成字串,而且更优,如果A和B不能形成字串,就算是没有排序之前可以形成字串,形成的也是一个负值的子串。

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <vector>
#define CLE(a,b) memset(a,b,sizeof(a))
#define LL long long
using namespace std;
const int Maxn = 50005;
LL table[Maxn];
struct node{
    LL  val;
    int id;
}resu[Maxn];
int n;
int lowbit(int x){
    return x&(-x);
}
bool cmp(node a,node b){
    return a.val<b.val;
}
void update(int x,LL add){
    while(x<=n){
        table[x]+=add;
        x+=lowbit(x);
    }
}
LL query(int x){
    LL sum = 0;
    while(x){
        sum+=table[x];
        x-=lowbit(x);
    }
    return sum;
}
void init(){
    CLE(table,0);
    for(int i=0;i<Maxn;i++){
        resu[i].val = 0;
        resu[i].id = 0;
    }
}
int main(){
    init();
    LL smin = INT_MAX;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int temp;
        scanf("%d",&temp);
        if(temp>0&&temp<smin)
            smin = temp;
        update(i,temp);
    }

    for(int i=1;i<=n;i++){
        resu[i].val = query(i);
        resu[i].id = i;
    }
    /*for(int i=1;i<=n;i++){
        printf("##%d %d\n",resu[i].id,resu[i].val);
    }*/
    sort(resu,resu+n+1,cmp);
    /*for(int i=0;i<=n;i++){
        printf("##%d %d\n",resu[i].id,resu[i].val);
    }*/
    /*for(int i=1;i<n;i++){
        if(resu[i].id<resu[i+1].id){
            int temp = resu[i+1].val-resu[i].val;
            if(temp<smin&&temp>0){
                smin = temp;
                //printf("%d %d\n",resu[i].id,resu[i+1].id);
            }
        }
        else{
            int temp = resu[i].val-resu[i+1].val;
            if(temp<smin&&temp>0){
                smin = temp;
               // printf("#%d %d\n",resu[i].id,resu[i+1].id);
            }
        }
    }
    printf("%lld\n",smin);*/
    int res = INT_MAX;
    int flag = 0;
    for (int i = 1; i <= n; i++)
    {
        if(resu[i].id-resu[i-1].id>0&& resu[i].val - resu[i - 1].val > 0)
        {
            if (flag == 0)
            {
                flag = 1;
                res = resu[i].val - resu[i - 1].val;
            }
            else
            {
                if (resu[i].val - resu[i - 1].val < res)
                {
                    res = resu[i].val - resu[i - 1].val;
                }
            }
        }
    }
    printf("%d\n",res);
    return 0;
}

筛选方式极为重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值