思路:
很容易想到用一个大根堆来存储每个位置的获利,但我们不能每次都取出根元素后,将其两边的元素标记位不可选择,因为这个根元素两边的元素之和很可能大于这个根元素,这时我们选择的就不是根元素,而是两边的元素,所以我们要有一个反悔选择的机会,我们在选择根元素的时候,在总获利中加上其获利,并把这个根元素的获利变为两边元素的获利之和减去根元素的获利,并将这个根元素重新放入大根堆,这样我们就有了反悔的机会,同时需要删除两边的元素,因为它们已经用不到了。
代码如下:
#include<iostream>
#include<queue>
#include<stdio.h>
#define MAX 500005
using namespace std;
int n,k,flag[MAX]={0},num[MAX];//num记录在某个地方的获利
long long tot=0;//总获利
int l[MAX],r[MAX];
struct node{
int key;
int loc;
node(int k,int l){
key=k;
loc=l;
}
bool operator < (const node &a) const{
return key<a.key;//最大值优先
}
};
priority_queue<node> q;
int main(){
scanf("%d%d",&n,&k);
int tmp;
for(int i=1;i<=n;i++){
scanf("%d",&tmp);
q.push(node(tmp,i));
l[i]=i-1;
r[i]=i+1;
num[i]=tmp;
}
l[0]=1,r[n+1]=n;
for(int i=0;i<k;i++){
while(flag[q.top().loc]==1){//删除已经被删除的根顶元素
q.pop();
}
node t=q.top();
q.pop();
if(t.key<=0){//如果堆顶元素小于等于0,则跳出循环
break;
}
tot+=t.key;//总获利增加t.key
int id=t.loc;
num[id]=num[l[id]]+num[r[id]]-num[id];
t.key=num[id];
flag[l[id]]=flag[r[id]]=1;//标记左右坑不可选择
l[id]=l[l[id]];//id的左坑变为它左坑的左坑
r[id]=r[r[id]];//id的右坑变为它右坑的右坑
l[r[id]]=id;//id右坑的左坑变为id
r[l[id]]=id;//id左坑的右坑变为id
q.push(t);
}
printf("%lld",tot);
return 0;
}