哈夫曼树、优先队列
今日腊月二十八,上午写了对联,下午敲代码,晚上玩手机…
还得再自控力强一些呀
下午主要精力用来写PTA上的一道题:修理牧场。题目如下:
7-29 修理牧场 (25分)
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。
先考虑用哈夫曼树来解决,,,不过这个知识忘得差不多了,,还要建树,有亿点麻烦。
哈夫曼树
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。(百度百科)
哈夫曼树在编码中的应用是根据编码出现的频率来给予不同的长度,从而节约空间。
算法(建树)原理
根据给定的数据(数组),选取两个数组中最下的树形成一棵子树,其双亲的权值设置为两个结点的权值之和,再将其父母加入数组,再次选取无父母的数组中最小的两个结点形成子树,,,以此类推。由于哈夫曼树是二叉树,且其只有零度结点与二度结点,故有:n0=n2+1;易知,结点总数为2*n-1;
故可以考虑用一个结构体数组来存储各个结点的信息。以修理牧场该题为例,假设需要得到的木头数为N,可开辟一个大小为2*N的数组,将新生成的父母结点加入到N+ 1、N+2、…
树的结构如下:
struct Node{
int data;
int parent;
int lchild;
int rchild;
};
与一般的树形结构类似,并无特别之处。
从数组中选取两个“最小”的数:
void find(Node* Btree,int m,int &s1,int &s2){
int minn1=55,minn2=55;
s1=s2=0;
for(int i=1;i<m;i++){
if(Btree[i].data<minn1&&Btree[i].parent==-1){
minn1=Btree[i].data;
s2=i;
}
}
for(int i=1;i<m;i++){
if(Btree[i].data<minn2&&Btree[i].parent==-1){
if(i!=s2){
s1=i;
minn2=Btree[i].data;
}
}
}
}
建树:
Node* Create(Node* Btree){
Btree=new Node[2*N];
for(int i=1;i<=N;i++){
scanf("%d",&Btree[i].data);
Btree[i].lchild=Btree[i].parent=Btree[i].rchild=-1;
}
for(int i=N+1;i<2*N;i++){
int s1,s2;
find(Btree,i,s1,s2);
Btree[i].parent=-1;
Btree[i].data=Btree[s1].data+Btree[s2].data;
Btree[s1].parent=Btree[s2].parent=i;
Btree[i].lchild=s1,Btree[i].rchild=s2;
}
return Btree;
}
遗憾的是,,,这个方法只通过了一个测试点,,迷惑。。
优先队列
还是偷瞄了一眼别人的代码,才拍拍脑袋想起这么个好用极了的东东。
实现方法:
建立一个只包含一个数据成员的结构体,并重载<号。这里对为何重载<号不是很明了。答:优先队列默认从大到小排列,若想要使数小的结点先出队,便要重载小于号!
struct Node{
int data;
friend bool operator <(Node a,Node b){
return a.data>b.data;//数组的排序。。。如果a>b,则无需交换顺序
}
};
之后建立一个优先队列,数据类型为结构体。一次插入数据,优先队列会自动排序,使小的在前面,大的数据在后面,之后每次拿出队列前两个数据,生成的双亲结点再次入队即可。
全代码:
#include<bits/stdc++.h>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string.h>
#define ll long long
#define INF 10000
#include<map>
#include<stack>
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
struct Node{
int data;
friend bool operator <(Node a,Node b){
return a.data>b.data;//数组的排序。。。如果a>b,则无需交换顺序
}
};
priority_queue<Node>Q;
int main(){
int N;
scanf("%d",&N);
for(int i=0;i<N;i++){
int a;
scanf("%d",&a);
Node n;
n.data=a;
Q.push(n);
}
int ans=0;
while(!Q.empty()){
int a=Q.top().data;
Q.pop();
if(Q.empty()) break;
int b=Q.top().data;
Q.pop();
ans+=a+b;
Node n;
n.data=a+b;
Q.push(n);
}
cout<<ans<<endl;
return 0;
}
这次就AC了。