1. 程式人生 > >[CTSC2007][APIO2007]數據備份Backup

[CTSC2007][APIO2007]數據備份Backup

ron 連接 結果 brush 數據備份 () ons bzoj1150 問題:

題目:BZOJ1150、codevs1615、洛谷P3620

題目大意:有n個點,k條鏈,每個點離原點有一定的距離。要你用k條鏈連接2k個點,使得k條鏈的長度最短。

解題思路:畢竟是CTSC級別的題目,很難找出正確算法。在網上翻閱了很多資料後,終於理解了此題的正確算法。orz

正確算法為:貪心+鏈表+堆。

首先每次肯定是鏈相鄰的2個點,所以我們先把相鄰2個點的差值求出來,得到有n-1個數的數列。

然後問題就變成“在這個數列中尋找k個互不相鄰的點,使得它們的和最小”。

我們把所有的數扔進一個堆裏,每次貪心找出最小的數,在答案裏把它加上。

這時就會出現一個問題:假設我們貪心出了第i個數,那麽有可能選第i-1個數和第i+1個數最終的結果優於選第i個數。

所以我們每次貪心時,把找到的那個數的前一個數和後一個數刪掉,把“前一個數+後一個數-原數”的值扔進堆裏,如果這個數被選,相當於選了前一個數和後一個數而不選原數。

而鏈表記錄的是每個數的前一個和後一個。詳見代碼第32~35行。

在刪數時,並不需要在堆裏找到這個數,只需把它的值改為inf就行了。詳見代碼第36行。

C++ Code:

#include<cstdio>
#include<ext/pb_ds/priority_queue.hpp>
const int inf=int(1000000000*1.3);
using namespace std;
struct shuju{
	int dis,num;
	shuju(int d,int n):dis(d),num(n){}
	bool operator<(const shuju& rhs)const{return dis>rhs.dis;}
};
__gnu_pbds::priority_queue<shuju,less<shuju>,__gnu_pbds::pairing_heap_tag>h;
int n,k,dis[100005],pre[100005],nxt[100005];
int main(){
	scanf("%d%d",&n,&k);
	int x,y;
	scanf("%d",&x);
	for(int i=2;i<=n;i++){
		scanf("%d",&y);
		dis[i]=y-x;x=y;
		h.push(shuju(dis[i],i));
		pre[i]=i-1;nxt[i]=i+1;
	}
	int ans=0;
	pre[2]=nxt[n]=0;
	while(k--){
		while(h.top().dis!=dis[h.top().num])h.pop();
		int k=h.top().num;
		int l=pre[k],r=nxt[k];
		h.pop();
		ans+=dis[k];
		if(l&&r)dis[k]=dis[l]+dis[r]-dis[k];else
		dis[k]=inf;
		pre[nxt[r]]=k;
		nxt[pre[l]]=k;
		nxt[k]=nxt[r];
		pre[k]=pre[l];
		dis[l]=dis[r]=inf;
		h.push(shuju(dis[k],k));
	}
	printf("%d\n",ans);
	return 0;
}

  

[CTSC2007][APIO2007]數據備份Backup