数据备份
给定一个序列 求其中最小的k个的和(这k个数任意两个不能相邻)
从k=2的时候出发: 发现要么选择最小的,要么选择最小元素两边的
用类似数学归纳的方式不断进行这种操作
设置一种反悔贪心 价值作为两边的价值减去中间的价值
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=2e5+10;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
struct Node
{
int pre,nxt,pos,val;
}p[N];
bool operator<(const Node &a,const Node &b){ return a.val>b.val;}
int num;
void erase(int x)
{
p[ p[x].pre ].nxt=p[x].nxt;
p[ p[x].nxt ].pre=p[x].pre;
}
Node insert(int x,int val)
{
int t=++num;
p[t]=(Node){x,p[x].nxt,t,val};
p[p[x].nxt].pre=t;
p[x].nxt=t;
return p[t];
}
int n,k;
priority_queue< Node > q;
int a[N],tot;
bool vis[N];
int main()
{
n=read(); k=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++)
{
int x=a[i+1]-a[i];
insert(i-1,x);
}
for(int i=1;i<n;i++) q.push(p[i]);
p[0].val=p[n].val=0x3f3f3f3f;
int ans=0;
while(q.size()&&tot<k)
{
Node t=q.top(); q.pop();
t=p[t.pos];
if(vis[t.pos]) continue;
vis[t.pos]=vis[t.pre]=vis[t.nxt]=1;
tot++; ans+=t.val;
int pp=t.pre,qq=t.nxt;
erase(t.pos); erase(t.pre); erase(t.nxt);
q.push( insert( p[pp].pre,p[pp].val+p[qq].val-t.val) );
}
printf("%d",ans);
return 0;
}
注意:
- 链表注意边界问题
- 二叉堆里的元素更新不及时,出队后需要重新更新

浙公网安备 33010602011771号