BZOJ1150 [CTSC2007]数据备份Backup 贪心 堆

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1150


题意概括

  数轴上面有一堆数字。

  取出两个数字的代价是他们的距离。

  现在要取出k对数,(一个数字被取出之后就不可再取),问最小代价。


 

题解

  这题貌似哪里做过。

  如果取了可以再取,那么我们肯定贪心的选择最短的。

  于是我们考虑先把所有的n个点变成n - 1条线段,然后取这些线段。

  我们贪心的来。

  每次要取掉最短的线段,那么我们用一个堆来维护。

  取掉最短的线段之后,我们删除它两端的线段,并修改其值为他  左边的 + 右边的 - 它自己。

  那么下一次取这一条线段的时候,就巧妙的变成了取原先的这条线段的两端的线段。

  这样就可以了。所以为了维护这个左右线段,我们要用上双向链表。


 

代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
using namespace std;
const int N=100000+5,Inf=1e9;
void read(int &x){
	x=0;
	char ch=getchar();
	while (!('0'<=ch&&ch<='9'))
		ch=getchar();
	while ('0'<=ch&&ch<='9')
		x=x*10+ch-48,ch=getchar();
}
int n,k,a[N],L[N],R[N],f[N],bh[N];
struct Seg{
	int len,pos,bh;
	bool operator < (const Seg a) const{
		return len>a.len;
	}
}s;
Seg new_Seg(int a,int b,int c){
	Seg res;
	res.len=a,res.pos=b,res.bh=c;
	return res;
}
priority_queue <Seg> q;
void Delete(int pos){
	if (pos==1||pos==n+1)
		return;
	f[pos]=1;
	R[L[pos]]=R[pos];
	L[R[pos]]=L[pos];
}
int main(){
	read(n),read(k);
	for (int i=1;i<=n;i++)
		read(a[i]);
	for (int i=n;i>1;i--)
		a[i]-=a[i-1];
	for (int i=1;i<=n+1;i++)
		L[i]=i-1,R[i]=i+1;
	a[1]=a[n+1]=Inf;
	while (!q.empty())
		q.pop();
	memset(f,0,sizeof f);
	memset(bh,0,sizeof bh);
	for (int i=2;i<=n;i++)
		q.push(new_Seg(a[i],i,0));
	int ans=0;
	while (k--){
		s=q.top();
		while (f[s.pos]||bh[s.pos]!=s.bh)
			q.pop(),s=q.top();
		ans+=s.len;
		int pos=s.pos,le=L[pos],ri=R[pos];
		q.push(new_Seg(a[pos]=a[le]+a[ri]-a[pos],pos,++bh[pos]));
		Delete(le);
		Delete(ri);
	}
	printf("%d",ans);
	return 0;
}

  

posted @ 2017-08-17 22:33  zzd233  阅读(245)  评论(0编辑  收藏  举报