题目描述:
N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。
例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
输入
第1行:2个数N和M,中间用空格分隔。N为整数的个数,M为划分为多少段。(2 <= N , M <= 50000)
第2 - N+1行:N个整数(-10^9 <= a[i] <= 10^9)
输出
输出这个最大和
输入样例
7 2
-2
11
-4
13
-5
6
-2
输出样例
26
分析:看到这个数据规模就需要知道dp不行(dp一般最好也是n^2)。那么就需要考虑贪心。在原来dp的基础上加一个堆,先将序列进行正负合并(正一负一正一负……)。将其中大于0的数累加,问题转化成合并即可序列,让他们变成M个序列的最大值。这时只需要加一个链表来维护即可。不过中间细节需要稍加处理
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
//typedef pair < LL , LL > pii;
LL a[500010];
LL n,m;
LL len=0;
LL la=0;
LL l[500010];
LL r[500010];
struct node{
LL k;
bool operator < (const node &x) const
{
return abs(a[k])>abs(a[x.k]);
}
};
priority_queue < node > q;
int main(){
scanf("%lld %lld",&n,&m);
la=0;
for (LL i=1,x;i<=n;i++){
scanf("%lld",&x);
if (x==0) continue;
if (la==0) {la=x;continue;}
if (la>0)
if (x>0) {la+=x;if (i==n) a[++len]=la;}else /*cout<<"la:"<<la<<endl,*/{a[++len]=la;la=x;if (i==n) a[++len]=la;}
else
if (x<0) {la+=x;if (i==n) a[++len]=la;}else /*cout<<"la:"<<la<<' '<<i<<endl,*/{a[++len]=la;la=x;if (i==n) a[++len]=la;}
}
// cout<<len<<endl;
// for (LL i=1;i<=len;i++) cout<<a[i]<<' ';
// cout<<endl;
LL num=0;
LL ans=0;
// for (LL i=1;i<=len;i++) cout<<a[i]<<' ';
// cout<<endl;
l[0]=0;l[len+1]=len;r[0]=1;r[len+1]=len+1;
for (LL i=1;i<=len;i++){
l[i]=i-1;r[i]=i+1;
if (a[i]>0) ++num,ans+=a[i];
q.push((node){i});
}
while (num>m){
LL numm=q.top().k;
q.pop();
if(l[r[numm]]!=numm||r[l[numm]]!=numm) continue;
if ((a[numm]<0&&(r[numm]>len||l[numm]<1))||!a[numm]) continue;
ans-=abs(a[numm]);
a[numm]+=a[l[numm]]+a[r[numm]];
l[numm]=l[l[numm]];
r[numm]=r[r[numm]];
r[l[numm]]=numm;
l[r[numm]]=numm;
q.push((node){numm});
num--;
}
printf("%lld",ans);
return 0;
}