7-19 修理牧场 (25分)(C语言实现)
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li 个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。
输入格式:
输入首先给出正整数N(≤10
4
),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。
输出格式:
输出一个整数,即将木头锯成N块的最少花费。
输入样例:
8
4 5 1 2 1 3 1 1
输出样例:
49
C语言实现
#include<stdio.h>
#include<stdlib.h>
#define MaxData -10 //定义哨兵
typedef int ElementType;
typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int Weight;
HuffmanTree Left;
HuffmanTree Right;
};
typedef struct HeapStruct *MinHeap;
struct HeapStruct{
ElementType *Elements;//储存堆元素的数组
int Size;//堆当前元素个数
int Capacity;//堆的最大容量
};
MinHeap Creates(int MaxSize);
int Huffman(MinHeap H);
ElementType DeleteMin(MinHeap H);
void Insert(MinHeap H,ElementType item);
int IsEmpty(MinHeap H);
int IsFull(MinHeap H);
int main(){
int n,tp;
scanf("%d",&n);
MinHeap h=Creates(n);
for(int i=0;i<n;i++){
scanf("%d",&tp);
Insert(h,tp);
}
int p=Huffman(h);
printf("%d",p);
return 0;
}
int Huffman(MinHeap H){
int i;
int sum=0;
HuffmanTree T;
for(int i = 1; H->Size>1;i++){
int a=DeleteMin(H);//从最小堆中删除一个结点,作为新T的左子结点
int b=DeleteMin(H);
sum+=a+b;
Insert(H,a+b);
}
return sum;
}
//最小堆
ElementType DeleteMin(MinHeap H){
int Parent,Child;
ElementType MaxItem,temp;
if(IsEmpty(H)==0){
printf("-1-");
return 0;
}
MaxItem = H->Elements[1];//取出结点
//用最大堆中的一个元素从根结点向上过滤下层节点,调整
temp = H->Elements[H->Size--];
//判断有没有左右孩子
for(Parent = 1;Parent*2 <= H->Size;Parent=Child){
Child=Parent*2;
//判断有没有右孩子,并找左右孩子中-小-的
if((Child!=H->Size) && (H->Elements[Child] > H->Elements[Child+1]))
Child++;
if(temp <= H->Elements[Child]) break;
else H->Elements[Parent] = H->Elements[Child];//将-小-孩子拉上来,并进入下一层,
}
H->Elements[Parent] = temp;
return MaxItem;
}
int IsEmpty(MinHeap H){
if(H->Size==0) return 0;
else return 1;
}
//最小堆的插入
void Insert(MinHeap H,ElementType item){
int i;
if(IsFull(H)==0){
printf("-2-");
return;
}
i = ++H->Size;//i指向插入后堆中的最后一个元素,并对size加1
/*比较父结点,如果放到最-xiao-,就会有哨兵,
这个哨兵就是最-xiao-的,让他不会在比较了。如果不用哨兵,就要加一个&&i>1*/
for(;H->Elements[i/2]>item;i/=2){
H->Elements[i]=H->Elements[i/2];//向下过滤
}
H->Elements[i]=item;//插入item
}
int IsFull(MinHeap H){
if(H->Size==H->Capacity)return 0;
else return 1;
}
MinHeap Creates(int MaxSize){
MinHeap H = malloc(sizeof(struct HeapStruct));
H->Elements=malloc((MaxSize+1)*sizeof(ElementType));//创建数组
H->Size = 0;
H->Capacity =MaxSize;
H->Elements[0]=MaxData;//哨兵
return H;
}
主要思想
这个题主要是将输入的数据进行升序排序后,每次找前两个最小的数经行相加,得到两数和sum,并从序列中删除这两个数。然后在将这个sum插入升序序列中。然后重复操作直到序列中只剩下一个数。然后将这里所有的sum求和就是答案了。
可以看出这就是利用最小堆构建哈夫曼树,求哈夫曼树所有非叶结点的和了。
用最小堆是因为利用最小堆方便插入sum,和删除数据。其时直接进行排序也是一样的。
我写的这个代码有点冗余,是因为我直接改写了以前写好的哈夫曼树。