生日礼物(固定个数的连续区间最大值

# 题意
给定n个数,从中选不超过m个连续的部分使得它们的和最大

# 题解
输入过程直接把符号相同的区间合并为一个点
计算出所有正数之和以及正数的个数cnt,如果cnt>m,
则需要挑选k=cnt-m处使区间变为m个,

否则要选负数区间使区间个数变为m,两种操作都是在当前的和中减去一个值,

所以通过这样可以将正负操作归成一种操作
将所有数的绝对值放在堆,每次选择最小的,
如果是正数,代表将当前的区间去掉,并和左右区间合并为一个
因为下次如果想要减少只能是两个正的合并
如果是负数,直接减去,代表和左右两遍的正数合并了,

负数的时候要判定左右,不能在区间的两端,因为如果负数绝对值最小且在两端点不能将两个正数区间合并,

也就是并没有实际减少,所以要特殊判断 

 1 #include <bits/stdc++.h>
 2 #define pii pair<int,int>
 3 #define fi first
 4 #define se second
 5 using namespace std;
 6 const int N=1e5+10;
 7 int l[N],r[N];
 8 priority_queue<pii,vector<pii>,greater<pii>>heap;
 9 int a[N];
10 bool st[N];
11 void remove(int i){
12    r[l[i]]=r[i];
13    l[r[i]]=l[i];
14    st[i]=true;
15 }
16 int main(){
17    int n,m;
18    cin>>n>>m;
19    int k=1;
20    for(int i=0;i<n;i++){
21       int x;
22       cin>>x;
23       if((long long)x*a[k] < 0){
24          a[++k]=x;
25       }
26       else a[k]+=x;
27    }
28    n=k;
29 
30    int cnt=0,sum=0;
31    for(int i=1;i<=n;i++) {
32       if (a[i] > 0) {
33          cnt++;
34          sum += a[i];
35       }
36       l[i]=i-1;
37       r[i]=i+1;
38       heap.push({abs(a[i]),i});
39    }
40 
41    while(cnt>m){
42       while(st[heap.top().se]) heap.pop();
43 
44       auto t=heap.top();
45       heap.pop();
46       int val=t.fi,idx=t.se;
47       if(l[idx]!=0 && r[idx]!=n+1 || a[idx]>0){//负数绝对值最小且在两端点不能将两个正数区间合并,也就是并没有实际减少,所以要特殊判断
48          cnt--;
49          sum-=val;
50          int left = l[idx],right=r[idx];
51          a[idx]+=a[left]+a[right];
52          heap.push({abs(a[idx]),idx});
53          remove(left);
54          remove(right);
55       }
56    }
57    cout<<sum<<endl;
58 }

 

 

猜你喜欢

转载自www.cnblogs.com/hhyx/p/12541982.html