题目大意:
给定一条长度为n的链,每一个点有一个权值,求不相邻地选小于等于k个点的总和的最大值
思路:
其实这题之前好像在哪里见过,但是由于当时没有好好理解,便还是想了一会,当时是一个环的情况,链的情况也差不多。DP其实很容易想,但是时间和空间上都过不去,所以可以考虑贪心,先去选那个权值最大的点,但是这不一定是最优的,我们发现,如果选最大的点不是最优的情况下,那么只有可能是选这个点限制了旁边两个点的选择,所以旁边两个点必须要全部都选,这样我们可以每次贪心的选取一个权值最大的点,并把以这个点为中心的三个点全部都缩成一个新的点,新的点的权值为a[i+1]+a[i-1]-a[i](假设原来的点的编号为i)这样当我们后面选的点的权值过小的时候,我们就可选择这个新缩成的点,就相当于不选i号点而选择了i+1号点和i-1号点,这样每次地贪心的选取,就相当于每一都会对前面的所有状态做一个颠倒,从而找到最后的最优的状态
做法:
就用优先队列就好了,删除点的时候不要从优先队列删掉,直接标记就好了,注意维护双向链表和堆中间的值。
/*===================
* Author : ylsoi
* Algorithm : tanxin
* Time : 2018.4.1
* ================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
void File(){
#ifndef ONLINE_JUDGE
freopen("luogu1484.in","r",stdin);
freopen("luogu1484.out","w",stdout);
#endif
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=5e5+10;
int n,k,l[maxn],r[maxn],head;
ll a[maxn],ans;
bool vis[maxn];
struct node{
int id;
bool operator < (const node &tt) const {
return a[id]<a[tt.id];
}
};
priority_queue<node>q;
int main(){
File();
scanf("%d%d",&n,&k);
REP(i,1,n)scanf("%lld",&a[i]);
REP(i,1,n)l[i]=i-1,r[i]=i+1;
r[n]=0;
REP(i,1,n)q.push((node){i});
while(k--){
while(vis[q.top().id])q.pop();
node t=q.top();
q.pop();
int u=t.id;
if(a[u]<=0)break;
ans+=a[u];
if(!l[u]){
vis[u]=vis[r[u]]=1;
l[r[r[u]]]=0;
}
else if(!r[u]){
vis[u]=vis[l[u]]=1;
r[l[l[u]]]=0;
}
else{
vis[l[u]]=vis[r[u]]=1;
a[u]=a[l[u]]+a[r[u]]-a[u];
q.push((node){u});
r[l[l[u]]]=u;l[u]=l[l[u]];
l[r[r[u]]]=u;r[u]=r[r[u]];
}
}
cout<<ans<<endl;
return 0;
}