资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
Huffman树在编码中有着广泛的应用。在这里,我们只关心Huffman树的构造过程。
给出一列数{pi}={p0, p1, …, pn-1},用这列数构造Huffman树的过程如下:
1. 找到{pi}中最小的两个数,设为pa和pb,将pa和pb从{pi}中删除掉,然后将它们的和加入到{pi}中。这个过程的费用记为pa + pb。
2. 重复步骤1,直到{pi}中只剩下一个数。
在上面的操作过程中,把所有的费用相加,就得到了构造Huffman树的总费用。
本题任务:对于给定的一个数列,现在请你求出用该数列构造Huffman树的总费用。
例如,对于数列{pi}={5, 3, 8, 2, 9},Huffman树的构造过程如下:
1. 找到{5, 3, 8, 2, 9}中最小的两个数,分别是2和3,从{pi}中删除它们并将和5加入,得到{5, 8, 9, 5},费用为5。
2. 找到{5, 8, 9, 5}中最小的两个数,分别是5和5,从{pi}中删除它们并将和10加入,得到{8, 9, 10},费用为10。
3. 找到{8, 9, 10}中最小的两个数,分别是8和9,从{pi}中删除它们并将和17加入,得到{10, 17},费用为17。
4. 找到{10, 17}中最小的两个数,分别是10和17,从{pi}中删除它们并将和27加入,得到{27},费用为27。
5. 现在,数列中只剩下一个数27,构造过程结束,总费用为5+10+17+27=59。
输入格式
输入的第一行包含一个正整数n(n<=100)。
接下来是n个正整数,表示p0, p1, …, pn-1,每个数不超过1000。
输出格式
输出用这些数构造Huffman树的总费用。
样例输入
5
5 3 8 2 9
样例输出
59
解题思路:
哈弗曼树是一个比较经典的算法,其应用场景在破解密码、加密等方面颇有建树。今天这道题其实思路很简单,用贪心的思想每次都获取最小的两个数相加之后,再放回树的数组中,如此反复,直到数组中只有一个元素,输出构建过程的代价。这里我用两个方法为大家解答。
第一个方法:最小堆,学过数据结构的朋友都知道,写最小堆的过程是如此艰辛,但是使用起来,不得不说,那是真香呀,先把所有输入的数放入最小堆中,每次从最小堆中获取堆顶元素,其一定是最小值,如此反复,直到堆中只有一个元素。
第二个方法:利用sort()函数排序,每次都拿最小的元素,注意要删除多余的元素哦。
以下是两种方法:
#include<bits/stdc++.h>
using namespace std;
template<class T>
class MinHeap {
private:
T *heapArray; // 存放堆数据的数组
int CurrentSize; // 当前堆中元素数目
int MaxSize; // 堆所能容纳的最大元素数目
public:
MinHeap(const int n); // 构造函数,n表示 堆的最大元素数目
virtual ~MinHeap() { delete[]heapArray; } // 析构函数
void swap(int pos_x, int pos_y); // 交换位置x和y的元素
void BuildHeap(); // 建堆
bool isEmpty(); // 如果堆空,则返回真
bool isLeaf(int pos) const; // 如果是叶结点,返回TRUE
int leftchild(int pos) const; // 返回左孩子位置
int rightchild(int pos) const; // 返回右孩子位置
int parent(int pos) const; // 返回父结点位置
bool Remove(int pos, T& node); // 删除给定下标的元素
bool Insert(const T& newNode); // 向堆中插入新元素newNode
T& RemoveMin(); // 从堆顶删除最小值
void SiftUp(int position); // 从position向上开始调整,使序列成为堆
void SiftDown(int left); // 向下筛选,参数left表示开始处理的数组下标
void show();
int getCurrentSize();
};
template<class T>
int MinHeap<T>::getCurrentSize() {
return CurrentSize;
}
template<class T>
bool MinHeap<T>::isEmpty() {
if (CurrentSize == 0) {
return true;
}
else return false;
}
template<class T>
bool MinHeap<T>::isLeaf(int pos) const {
if (pos % 2 == 1) {
return true;
}
else return false;
}
template<class T>
void MinHeap<T>::swap(int pos_x, int pos_y) {
T data = heapArray[pos_x];
heapArray[pos_x] = heapArray[pos_y];
heapArray[pos_y] = data;
}
template<class T>
void MinHeap<T>::BuildHeap() {
for (int i = CurrentSize / 2 - 1; i >= 0; i--)// 反复调用筛选函数
SiftDown(i);
}
template<class T>
MinHeap<T>::MinHeap(const int n) {
if (n <= 0) return;
CurrentSize = 0;
MaxSize = n; // 初始化堆容量为n
heapArray = new T[MaxSize]; // 创建堆空间
BuildHeap(); // 此处进行堆元素的赋值工作
}
template<class T>
int MinHeap<T>::leftchild(int pos) const {
return 2 * pos + 1; // 返回左孩子位置
}
template<class T>
int MinHeap<T>::rightchild(int pos) const {
return 2 * pos + 2; // 返回右孩子位置
}
template<class T>
int MinHeap<T>::parent(int pos) const {
return (pos - 1) / 2; // 返回父结点位置
}
template <class T>
bool MinHeap<T>::Insert(const T& newNode) { // 向堆中插入新元素newNode
if (CurrentSize == MaxSize) return false; // 堆空间已经满
heapArray[CurrentSize] = newNode;
SiftUp(CurrentSize); // 向上调整
CurrentSize++;
return true;
}
template<class T>
T& MinHeap<T>::RemoveMin() { // 从堆顶删除最小值
if (CurrentSize == 0) {
cout << "Can't Delete"; // 调用RemoveMin之前,需要判断堆非空
exit(1);
}
else {
swap(0, --CurrentSize); // 交换堆顶和最后一个元素
if (CurrentSize > 1) {
SiftDown(0); // 从堆顶开始筛选
}
return heapArray[CurrentSize];
}
}
template<class T>
bool MinHeap<T>::Remove(int pos, T& node) { // 删除给定下标的元素
if ((pos < 0) || (pos >= CurrentSize)) return false;
node = heapArray[pos];
heapArray[pos] = heapArray[--CurrentSize]; // 用最后的元素值替代删除位置的元素
if (heapArray[parent(pos)] > heapArray[pos])
SiftUp(pos); // 当前元素小于父结点,需要上升调整
else SiftDown(pos); // 当前元素大于父结点,向下筛
return true;
}
template<class T>
void MinHeap<T>::SiftUp(int position) {
// 从position向上开始调整
int temppos = position;
T temp = heapArray[temppos];
while ((temppos > 0) && (heapArray[parent(temppos)] > temp)) {
heapArray[temppos] = heapArray[parent(temppos)];
temppos = parent(temppos);
}
heapArray[temppos] = temp;
}
template <class T>
void MinHeap<T>::SiftDown(int left) {
int i = left; // 标识父结点
int j = leftchild(i); // 标识关键值较小的子结点
T temp = heapArray[i]; // 保存父结点
while (j < CurrentSize) { // 过筛
if ((j < CurrentSize - 1) && (heapArray[j] > heapArray[j + 1]))
//若有右子节点,且小于左子节点
j++; // j指向右子结点
if (temp > heapArray[j]) { //若父节点大于子节点的值则交换位置
heapArray[i] = heapArray[j];
i = j;
j = leftchild(j);
}
else break; //堆序满足,跳出
}
heapArray[i] = temp;
}
template<class T>
void MinHeap<T>::show() {
for (int i = 0;i < CurrentSize; i++)
{
cout << heapArray[i] << " ";
}
}
int main() {
MinHeap<int> heap(100);
int i, n;
cin >> n;
for(i = 0; i < n; i++) {
int data;
cin >> data;
heap.Insert(data);
}
int sum = 0;
int num = 0;
while(heap.getCurrentSize() > 1){
num += heap.RemoveMin();
num += heap.RemoveMin();
sum += num;
heap.Insert(num);
num = 0;
}
cout << sum;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[100];
int i,n;
cin >> n;
for(i = 0; i < n; i++) {
cin >> a[i];
}
sort(a,a+n);//从小到大排序
int sum = 0;
while(n > 1)
{
i=0;
a[i] = a[i] + a[i+1];
sum += a[i];
a[i + 1] = -1;//将这个数去除
sort(a,a+n);
for(i = 0;i < n-1; i++)//把-1值覆盖
a[i] = a[i+1];
n--;
}
cout << sum;
return 0;
}