bzoj2288: 【POJ Challenge】生日礼物【贪心+优先队列+链表】

Description

ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, …, AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。

自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?

Input

第1行,两个整数 N (1 ≤ N ≤ 105) 和 M (0 ≤ M ≤ 105), 序列的长度和可以选择的部分。

第2行, N 个整数 A1, A2, …, AN (0 ≤ |Ai| ≤ 104), 序列。

Output

一个整数,最大的和。

Sample Input

5 2

2 -3 2 -1 2

Sample Output

5

解题思路:

首先连续符号相同的数可合并在一起,这样序列就是正负交替出现了。
求出大于0的段数cnt以及和sum;
若cnt<=m,那sum就是答案。
若cnt>m,那么每次取绝对值最小的一段,并将它与左右两端合并,用堆+链表维护。
如果该段是负数,那么相当于强行合并两段正数,损失最小,提高了利用价值;
如果该段是正数,那么相当于不选它或是削弱两边负数的影响,有利于后面的合并。
两种情况都会使cnt-1,ans-|a[i]|。
注意如果取得是首尾处的负数要直接扔掉,且cnt不会减。

#include<bits/stdc++.h>
#define pii pair<int,int> 
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=100005;
int n,m,cnt,ans,l[N],r[N],a[N];
bool mark[N];
priority_queue<pii,vector<pii>,greater<pii> >q;

void del(int x)
{
    mark[x]=1;
    l[r[x]]=l[x],r[l[x]]=r[x];
}

int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint();
    int x,tmp=0;
    for(int i=1;i<=n;i++)
    {
        x=getint();if(!x)continue;
        if(1ll*a[tmp]*x>0)a[tmp]+=x;
        else a[++tmp]=x;
    }n=tmp;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>0)cnt++,ans+=a[i];
        l[i]=i-1,r[i]=i+1;
        q.push(make_pair(abs(a[i]),i));
    }
    while(cnt>m)
    {
        while(mark[q.top().second])q.pop();
        int x=q.top().second;q.pop();
        if((l[x]&&r[x]!=n+1)||(a[x]>0))
        {
            --cnt,ans-=abs(a[x]);
            a[x]+=a[l[x]]+a[r[x]];
            q.push(make_pair(abs(a[x]),x));
            del(l[x]),del(r[x]);
        }
        else del(x);
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值