poj-3253哈夫曼樹

題目的意思是。要把一塊木板切成所需要的n塊,但是每切一刀需要收費。收費的方法是(網上有一些人理解有誤):

每當在一塊木板上動刀子,這塊木板的原有長度代表了這刀的費用。


思路:

切割的過程可以用一棵二叉樹表示,每個節點有兩個或者沒有子節點,
節點存儲切割過程中每塊木板的長度。父節點的值=兩個子節點相加 
根節點是原始木板長度。每塊所需要的木板都是葉子節點。
非葉子節點的值之和就是要花的總價格。


定理1: 
如果要使得總價格最少,就要求高層(靠近根)的每一個節點的值 大於 低層每一個節點的值



證明: 
運用反證法,假如某一棵二叉樹,總價格最低,且不滿足定理1。
假如第N層a節點大於第M層b節點,N>M  a-b = c 
那麼,我們可以構造出一棵二叉樹,即把第N層的a節點和M層的b節點交換(如果是非葉子節點,交換子樹),之後保證 父節點的值=兩個子節點相加 。 
a值的減少c會使得自己的每個先輩節點減少c, b值增加c也會使自己每個先輩節點增加c。由於a在b的下層,導致a的先輩比b的先輩多,
每個先輩節點必定不是葉子節點,這就使得總價格降低,和原二叉樹總價最低矛盾。


定理2:
每一顆二叉樹,只要高層(靠近根)的每一個節點的值 大於 低層每一個節點的值,那麼這些二叉樹所代表的總價值相同。



證明:
原理同定理1,通過同一層的節點(或者子樹)相互交換,構造出的任意一棵滿足要求的二叉樹,
由於先輩節點值增加,減少的值相互抵消,所以總價值不變。


由於哈弗曼樹滿足我們所需要的二叉樹的性質(這也同時說明,滿足總費用最少的二叉樹不一定是哈夫曼樹),所以這道題用哈夫曼樹來解。 


int find_small(int arr[], int len, int &a, int &b){//a是最小的,b是第二小的
	a=b=99999;
	int a_index, b_index, i; 
	a_index = b_index = 0;
	for(i=0; i<len; ++i){
		if(arr[i]!=-1&&arr[i]<a){
			b_index = a_index;
			a_index = i;
			b = a;
			a = arr[i];
			
		} else if(arr[i]!=-1&&arr[i]<b){
			b_index = i;
			b = arr[i];
		}
	}
	arr[a_index] = arr[b_index] = -1;
	return a_index;
}; 
int main(){
	int n,cnt;
	int arr[100];
	cin>>n;
	for(int i=0; i<n; ++i){
		cin>>arr[i];
	}
	cnt = n;
	int w=0;
	if(cnt==1){
		w = arr[0];
	}
	while(cnt!=1){
		--cnt;
		int a,b,hole;
		hole = find_small(arr,n,a,b);
		w = w+a+b;
		arr[hole] = a+b;
	}
	cout<<w; 
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章