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


    • UPDATE

      LeTex好象又挂了

    • 题目链接:

      https://www.luogu.org/problemnew/show/P3620
      https://www.lydsy.com/JudgeOnline/problem.php?id=1150

    • 思路(来自《算法竞赛进阶指南》):

      容易知道,最优解中配对的楼肯定是相邻的,于是我们把所有相邻楼之间的距离(D_1),(D_2),(D_3)...(D_n)记录下来,放进一个堆里。

      很明显,每次都取堆中的最小值是不正确的。那么这就有个很妙的思路:假设(D_i)是最小值,那么我们就取出(D_i),同时取出(D_{i-1})(D_{i+1}),然后再把一个一个值(D_{i+1})+(D_{i-1})-(D_i)的数放进堆,如果下一步这个新节点是最小值,很明显这是最优解。

      • 难点1:

      删了(D_{i+1}),(D_{i-1})(D_{i})后插入一个新节点,那它的前驱和后继怎么确定呢?最简单的方式当然就是用链表。

    • 难点2:

      我们要让堆和链表建立一个映射关系,怎么搞呢???我就在这里卡了好久,其实我们可以用反函数的思想。用一个数组v[]记录外面链表数组在堆中的下标,这样映射就建立了(其实这应该蛮好想的,我还是太弱了)

    • 代码:

    /*By Rye_Catcher*/
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <cctype>
    #define ll long long 
    using namespace std;
    const int maxn=100005;
    int a[maxn],pre[maxn],ne[maxn],v[maxn];
    struct Small_Heap{
    	int heap[maxn],n;
    	inline void up(int s){
    		int fa=s>>1;
    		while(s>1){
    			if(a[heap[s]]<a[heap[fa]]){
    				swap(heap[s],heap[fa]);
    				swap(v[heap[s]],v[heap[fa]]);
    				s=fa;fa=s>>1;
    			}
    			else break;
    		}
    	}
    	inline void insert(int k){
    		heap[++n]=k;
    		v[k]=n;
    		up(n);
    	}
    	inline void down(int fa){
    		int s=fa<<1;
    		while(s<=n){
    			if(a[heap[s]]>a[heap[s+1]]&&s<n)s++;
    			if(a[heap[s]]<a[heap[fa]]){
    				swap(heap[s],heap[fa]);
    				swap(v[heap[s]],v[heap[fa]]);
    				fa=s,s=fa<<1;
    			}
    			else break;
    		}
    	}
    	inline void sub(int k){
    		heap[v[k]]=heap[n];
    		v[heap[n]]=v[k];
    		n--;
    		up(v[k]),down(v[k]);
    	}
    }poi;
    int n,k;
    template <class T>inline void read(T &x){
    	x=0;int ne=0;char c;
    	while(!isdigit(c=getchar()))ne=c=='-';
    	x=c-48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    	x=ne?-x:x;
    	return ;
    }
    int main(){
    	int la,x;
    	ll ans=0;
    	read(n),read(k);
    	read(la);
    	for(int i=2;i<=n;i++){
    		read(x);
    		a[i-1]=x-la;
    		poi.insert(i-1);
    		ne[i-1]=i,pre[i-1]=i-2;
    		la=x;
    	}
    	for(register int i=1;i<=k;i++){  
         	x=poi.heap[1];ans+=a[x];
    		if(pre[x]==0)
    		{
    			poi.sub(x),poi.sub(ne[x]);
    			pre[ne[ne[x]]]=0;
    		}
    		else if(ne[x]==n)
    		{
    			poi.sub(x),poi.sub(pre[x]);
    			ne[pre[pre[x]]]=n;
    		}
    		else {		
    		poi.sub(x);//poi.extract();   
    		poi.sub(pre[x]),poi.sub(ne[x]);
    		a[x]=a[pre[x]]+a[ne[x]]-a[x];
    		poi.insert(x);
    		pre[x]=pre[pre[x]],ne[pre[x]]=x;
    		ne[x]=ne[ne[x]],pre[ne[x]]=x;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    【VectorDemo】
    【列表迭代器:添加元素】
    【列表迭代器】
    【迭代器】
    【LinkedList】
    【list 集合的使用】
    【CollectionDemo2】
    【CollectionDemo1】
    【集合和数组的区别?】
    【到底使用那种集合?】
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/8834708.html
Copyright © 2020-2023  润新知