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; }