基础实验4-2.7 修理牧场 (25 分)【哈夫曼树的构建】

农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数L​i个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是L​i 的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为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

哈夫曼树,又称最优树,是一类带权路径长度最短的树。
你拿到这道题目肯定会疑惑——这锯木头跟树结构有什么关系?跟哈夫曼树又有什么关系?
这就是这道题目的巧妙之处了,它利用了解构的手段,每一次锯木相当于二叉树的一个分支,叶结点的权重就是该结点代表的木头长度,非叶结点就是子节点的权重和。
在这里插入图片描述
当然你不需要写出真正的树结构,只需要得到结果就可以了,利用最小堆是最好的选择。
这道题思路比较难以理解,代码很好写
尤其要注意的是堆的删除操作,这个操作不太好实现,思路是把堆中最后一个元素提到堆顶部,然后循环调整
之所以是要用child中较小的那一个,是因为要将child提升为parent,需要满足非叶子节点小于其child的条件,总之,delete操作应当多写几遍,加以熟悉。
我发现自己也就能写写这种,4.26这种复杂题目真的写不出来(悲
代码如下:

#include <stdio.h>
#include<stdlib.h>
#include <string.h>
#include <iostream>

using namespace std;
#define Mindata 0
#define MAXdata 500000
#define Scale 10000

typedef struct node* Heap;
typedef int ElementType;
int ans = 0;
struct node {
	int* Data;
	int size;
	int capacity;//直接定义为无限大
};

Heap creatminheap(int maxsize) {
	Heap H = (Heap)malloc(sizeof(struct node));
	H->Data = (int*)malloc(2*(maxsize+1) * sizeof(int));
	H->size =0;
	H->Data[0] = Mindata;

	return H;
}

Heap insert(Heap H, int n) {
	int mom = ++H->size;
	for (; H->Data[mom/2] > n; mom /= 2)//注意是mom/2
		H->Data[mom] = H->Data[mom / 2];
	H->Data[mom] = n;
	return H;
}


ElementType DeleteMin(Heap H)
{ 
	int Parent, Child;
	ElementType MinItem, X;

	MinItem = H->Data[1];
	
	X = H->Data[H->size--]; 
	for (Parent = 1; Parent * 2 <= H->size; Parent = Child) {
		Child = Parent * 2;
		if ((Child != H->size) && (H->Data[Child] > H->Data[Child + 1]))
			Child++;  /* Child指向左右子结点的较小者 */
		if (X < H->Data[Child]) break; /* 找到了合适位置 */
		else  /* 下滤X */
			H->Data[Parent] = H->Data[Child];
	}

	H->Data[Parent] = X;

	return MinItem;
}

int main() {
	int n,tmp;
	cin >> n;
	Heap H = creatminheap(n);

	for(int i=0;i<n;i++){
		cin >> tmp;
		H=insert(H, tmp);
	}	
	n--;
	while (n--) {//砍n-1次
		tmp = DeleteMin(H) + DeleteMin(H);
		ans += tmp;
		H = insert(H, tmp);
	}
	cout << ans;
}
  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值