51Nod - 1065 - 最小正子段和(线段树 & 贪心)

51Nod - 1065 - 最小正子段和

N个整数组成的序列a11,a22,a33,…,ann,从中选出一个子序列(aii,ai+1i+1,…ajj),使这个子序列的和>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
输出最小正子段和。
Sample Input
8
4
-1
5
-2
-1
2
6
-2
Sample Output
1

题目链接

这里有两种做法,一种是线段树,一种是贪心。

  1. 线段树:
    这里还需要前缀和,先在输入的时候记录前缀和,用两个数组,然后其中一个进行排序,去重,离散化,建一棵树来记录区间内前缀和的最大值。接下来,按照原来没有排过序的前缀和从头到尾进行遍历,每一次都找到在当前前缀和位置之前的最大前缀和,这样做差能够得到相对最小的区间和,一直找最小值就是答案了。
  2. 贪心:
    这个需要一个结构体,记录前缀和和该前缀和所处的位置,也是需要将前缀和进行排序,然后对排好序的前缀和进行遍历,如果后面的比前面的位置更大,那么就说明相对位置正确的前提下,他们的这段区间的和(或说这两个前缀和)更相近,也就是差值更小,如果相对位置不对的话就跳过了,这样遍历完后就是答案。

线段树AC代码:(借鉴学长)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 5e4 + 5;
ll sum[maxn], pre[maxn];
int n, m;
map<ll,int> mp;

struct node
{
    int l;
    int r;
    ll val;
}p[maxn << 2];

void pushup(int cur)
{
    p[cur].val = max(p[cur << 1].val, p[cur << 1 | 1].val);
}

void build(int l, int r, int cur)
{
    p[cur].l = l;
    p[cur].r = r;
    p[cur].val = -inf;
    if(l == r) return;
    int m = (l + r) >> 1;
    build(l, m, cur << 1);
    build(m+1, r, cur << 1 | 1);
}

ll query(int pl, int pr, int cur)
{
    ll res;
    int l = p[cur].l, r = p[cur].r;
    if(pl <= l && r <= pr)
    {
        return p[cur].val;
    }
    res = -inf;
    if(pl <= p[cur << 1].r) res = max(res, query(pl, pr, cur << 1));
    if(pr >= p[cur << 1 | 1].l) res = max(res, query(pl, pr, cur << 1 | 1));
    return res;
}

void update(int tar,int cur)
{
    int l = p[cur].l, r = p[cur].r;
    if(l == r)
    {
        p[cur].val = pre[l];
        return ;
    }
    int m = (l + r) >> 1;
    if(tar <= m) update(tar, cur << 1);
    else update(tar, cur << 1 | 1);
    pushup(cur);
}

int main()
{
    ll val, ans;
    int i, l, r;
    scanf("%d", &n);
    for(i = 1; i <= n; i++)
    {
        scanf("%lld", &val);
        sum[i] = sum[i - 1] + val;
        pre[i] = sum[i];
    }
    mp.clear();
    sort(pre+1, pre+n+1);
    for(i = 1, m = 0; i <= n; i++)
    {
        if(!m || pre[m] != pre[i])
        {
            pre[++m] = pre[i];
            mp[pre[m]] = m;
        }
    }
    build(1, m, 1);
    ans = inf;
    for(i = 1; i <= n; i++)
        if(sum[i] > 0) ans = min(ans, sum[i]);
    for(i = 1; i <= n; i++)
    {
        l = 1, r = mp[sum[i]] - 1;
        if(l <= r)
        {
            val = query(1, mp[sum[i]] - 1, 1);
            if(val != -inf)
                ans = min(ans, sum[i] - val);
        }
        update(mp[sum[i]], 1);
    }
    printf("%lld\n", ans);
    return 0;
}

贪心AC代码(参考这篇博文

#include <iostream>
#include <cstdio>
#include <algorithm>
using  namespace  std;
#define ll long long
const int maxn = 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[maxn];

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)
        {
            if(!ans)    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;
}

猜你喜欢

转载自blog.csdn.net/weixin_40788897/article/details/81604210