bzoj1150[CTSC2007]数据备份Backup
题意:
n个地方,在其中找k对地方,每个地方只属于一对。定义一对的费用为两个地方的距离,求最小费用总和。
题解:
把所有相邻地方距离放入一个集合中,每次取出最小的那个距离x,然后将相邻两边的距离l,r合并成l+r-x。如果这个x缺一边相邻,则将剩下那一边的距离去掉,如果缺两边相邻,则不用去掉。这个找相邻的过程用链表维护,同时找最小距离以及删距离的操作用set维护(好像也可以用手写堆,然而我不会)。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <set> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 100010 7 using namespace std; 8 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 12 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 13 return f*x; 14 } 15 int n,k,a[maxn]; long long ans; 16 struct sn{ 17 int pos,v; 18 bool operator < (const sn &x)const{return v!=x.v?v<x.v:pos<x.pos;} 19 }; 20 set<sn>s; 21 struct nd{int v,l,r;}; nd nds[maxn]; 22 int main(){ 23 n=read(); k=read(); int st=read(); inc(i,1,n-1){int x=read(); a[i]=x-st; st=x;} n--; 24 inc(i,1,n){s.insert((sn){i,a[i]}); nds[i]=(nd){a[i],i-1,i+1>n?0:i+1};} 25 inc(i,1,k){ 26 set<sn>::iterator xx=s.begin(); int x=xx->pos; ans+=xx->v; s.erase(xx); 27 if(!nds[x].l&&!nds[x].r)continue; 28 else if(!nds[x].r){ 29 xx=s.find((sn){nds[x].l,nds[nds[x].l].v}); s.erase(xx); nds[nds[nds[x].l].l].r=0; 30 }else if(!nds[x].l){ 31 xx=s.find((sn){nds[x].r,nds[nds[x].r].v}); s.erase(xx); nds[nds[nds[x].r].r].l=0; 32 }else{ 33 nds[x].v=nds[nds[x].l].v+nds[nds[x].r].v-nds[x].v; 34 xx=s.find((sn){nds[x].l,nds[nds[x].l].v}); s.erase(xx); 35 xx=s.find((sn){nds[x].r,nds[nds[x].r].v}); s.erase(xx); 36 s.insert((sn){x,nds[x].v}); 37 nds[x].l=nds[nds[x].l].l; nds[x].r=nds[nds[x].r].r; nds[nds[x].l].r=x; nds[nds[x].r].l=x; 38 } 39 } 40 printf("%lld",ans); return 0; 41 }
20160902