数据备份

给定一个序列 求其中最小的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;
}

注意:

  1. 链表注意边界问题
  2. 二叉堆里的元素更新不及时,出队后需要重新更新
posted @ 2022-01-21 19:03  __iostream  阅读(31)  评论(0)    收藏  举报