欢迎来到xXchu的博客
xXchu
xXchu
晓看天色暮看云,行也思君,坐也思君

题解 P3620 【[APIO/CTSC 2007]数据备份】

直接贪心(每次选最小)的话显然不对...样例都过不了... 选两个办公楼的时候,显然不能跨越另一个楼,这样不优... 于是 先把原数列处理成n-1个的数(每一个办公楼和上一个的距离),存在a[]中 题目就是 要求选出K个不相邻的数,使得选出的数的和最小

依然考虑贪心,每次选最小的 但是若a[]是2 1 2 6,要选K=2个,先选了1,然后会发现两个2都没法选,只好选6,这样就尴尬了... 1选了就选了吧,我们考虑补救措施... 就是选1的时候,在堆里删掉两个2,然后在堆中插入2+2-1 相当于 我们以后可以取到那两个2,而且2+2-1可以表示选了两个数,因为刚才选了1,所以在这里-1

还有个边界问题...比如取了第一个数或最后一个数,那么把a[0]和a[n]赋成极大值呗...

因为边界的数 的两侧 只有一个有效的数,无法补救了(补救的时候需要俩数...),赋成极大值那么以后都无法取到

那就要存储 一个数的前一个数是多少,后一个数是多少(方便删除)

7 2 1 2 6 8 取1,删掉俩2,然后新开一个链表空间a[++n],存储2+2-1,用这个链表空间取代2 1 2

新的链表空间,前驱是7所在位置,后继是6所在位置...不要忘了修改 7和6 的前驱和后继...

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm> 
 5 #define N 100009
 6 using namespace std;
 7 int n,dis[N],la,lheap,heap[N<<3],pos[N<<3],K,now,point[N<<3][2],prev,next;
 8 long long a[N<<3],ans;
 9 inline void push(int x)
10 {
11     int i=++lheap;
12     heap[i]=x;
13     pos[x]=i;//pos用来记录链表a[]第x个位置 在堆中的位置
14     while (i>1)
15         if (a[ heap[i] ]<a[ heap[i/2] ])
16             {
17                 swap(heap[i],heap[i/2]);
18                 swap(pos[ heap[i] ],pos[ heap[i/2] ]);
19                 i>>=1;
20             }
21         else return;
22 }
23 inline int del(int x)//这里删除的时候,仅仅是赋了一个极大值,塞到堆最靠下的位置啦~
24 {
25     int i=pos[x];
26     a[x]=1e12;
27     while (i*2<=lheap)
28         {
29             int p=i*2;
30             if ( a[ heap[p+1] ]<a[ heap[p] ] ) p++;
31             if ( a[ heap[p] ]<a[ heap[i] ] )
32                 {
33                     swap(heap[i],heap[p]);
34                     swap(pos[ heap[i] ],pos[ heap[p] ]);
35                     i=p;               
36                 }
37             else break;
38         }
39 }
40 inline int pop()
41 {
42     int i=1,rt=heap[1];
43     heap[1]=heap[lheap--];
44     pos[ heap[1] ]=1;
45     while (i*2<=lheap)
46         {
47             int p=i*2;
48             if ( a[ heap[p+1] ]<a[ heap[p] ] ) p++;
49             if ( a[ heap[p] ]<a[ heap[i] ] )
50                 {
51                     swap(heap[i],heap[p]);
52                     swap(pos[ heap[i] ],pos[ heap[p] ]);
53                     i=p;
54                 }
55             else return rt;
56         }
57     return rt;
58 }
59 int main()
60 {
61     int i,j,k;
62     cin>>n>>K>>dis[1];
63     a[0]=a[n]=1e12;//边界赋一个极大值...(因为a[]中是n-1个数,后边界显然是n)
64     point[0][1]=1,point[0][0]=0,point[n][1]=n,point[n][0]=n-1;//题解中有解释...
65     push(0),push(n);
66     for (i=1;i<n;i++)
67         {
68             scanf("%d",&dis[i+1]);
69             a[i]=dis[i+1]-dis[i];
70             point[i][0]=i-1;
71             point[i][1]=i+1;
72             push(i);
73         }
74 
75     for (la=n;K;K--)
76         {
77             ans+=a[now=pop()];//pop完了后,要先push,因为堆是在不断扩展的,pop后那个元素依然在heap
78             prev=point[now][0],next=point[now][1]; //数组的最后一个,push后可以把它覆盖
79             a[++la]=a[prev]+a[next]-a[now];
80 
81             point[la][0]=point[prev][0];
82             point[ point[prev][0] ][1]=la;
83             point[la][1]=point[next][1];
84             point[ point[next][1] ][0]=la;
85 
86             push(la);//push要在del之前,
87             del( prev ),del( next );
88         }
89     cout<<ans<<endl;
90 }
View Code

 

posted @ 2019-08-15 09:03  xXchu  阅读(194)  评论(0编辑  收藏  举报