最小正子段和 51Nod - 1065

点击打开链接

一 线段树

一个子段肯定是由两个前缀做差相得 那么对于每一个前缀 就看它前边比它小的最大的前缀 做差取最大值 线段树维护一下

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 0x3f3f3f3f3f3f3f3f

struct node
{
    int l;
    int r;
    ll val;
};

map <ll,int> mp;
node tree[200010];
ll sum[50010],pre[50010];
int n,m;

void pushup(int cur)
{
    tree[cur].val=max(tree[2*cur].val,tree[2*cur+1].val);
    return;
}

void build(int l,int r,int cur)
{
    int m;
    tree[cur].l=l;
    tree[cur].r=r;
    tree[cur].val=-N;
    if(l==r) return;
    m=(l+r)/2;
    build(l,m,2*cur);
    build(m+1,r,2*cur+1);
    return;
}

ll query(int pl,int pr,int cur)
{
    ll res;
    if(pl<=tree[cur].l&&tree[cur].r<=pr)
    {
        return tree[cur].val;
    }
    res=-N;
    if(pl<=tree[2*cur].r) res=max(res,query(pl,pr,2*cur));
    if(pr>=tree[2*cur+1].l) res=max(res,query(pl,pr,2*cur+1));
    return res;
}

void update(int tar,int cur)
{
    if(tree[cur].l==tree[cur].r)
    {
        tree[cur].val=pre[tree[cur].l];
        return;
    }
    if(tar<=tree[2*cur].r) update(tar,2*cur);
    else update(tar,2*cur+1);
    pushup(cur);
    return;
}

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==0||pre[m]!=pre[i])
        {
            pre[++m]=pre[i];
            mp[pre[m]]=m;
        }
    }
    build(1,m,1);
    ans=N;
    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!=-N)
            {
                ans=min(ans,sum[i]-val);
            }
        }
        update(mp[sum[i]],1);
    }
    printf("%lld\n",ans);
    return 0;
}

二 贪心排序

上一个解法是按端点来考虑 这个解法是看别人的 按照取值来考虑 一个正子段肯定是一个在后的大前缀减去在前的小前缀 所以先按前缀的值升序排序 又因为同值的前缀作差为零没意义 所以同值前缀位置越靠后则排序是越往前放

这样所有的前缀首先按取值分成了很多段 每段取值相同 而两端交界处因为“同值前缀位置越靠后则排序是越往前放”所以会在这里产生最优解

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 0x3f3f3f3f3f3f3f3f

struct node
{
    ll sum;
    int pos;
};

node pre[50010];
int n;

bool cmp(node n1,node n2)
{
    if(n1.sum==n2.sum) return n1.pos>n2.pos;
    else return n1.sum<n2.sum;
}

int main()
{
    ll val,minn;
    int n,i;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&val);
        pre[i].sum=pre[i-1].sum+val;
        pre[i].pos=i;
    }
    sort(pre+1,pre+n+1,cmp);
    minn=N;
    for(i=1;i<=n;i++)
    {
        if(pre[i].sum>0)
        {
            minn=min(minn,pre[i].sum);
        }
    }

    for(i=1;i<=n;i++)
    {
        if(pre[i].pos>pre[i-1].pos&&pre[i].sum>pre[i-1].sum)
        {
            minn=min(minn,pre[i].sum-pre[i-1].sum);
        }
    }
    printf("%lld\n",minn);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sunyutian1998/article/details/80233435
今日推荐