【堆与优先队列】——修理牧场 (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

方式一:

堆(一种特殊的完全二叉树)还没用熟。。。c++的优先队列不会用,只是知道有这么个玩意,网上找了它的用法才写出来的,算是又了解了一项新的技能吧。这道题一看最少花费,就想到了哈夫曼树,也就是最优二叉树,但是我目前只是知道了它的用法,具体代码写了一部分,还有待学习,二叉树的前边还挺简单,比如遍历,构造等,一到应用,或者AVL树等就变得复杂了,有点担心。。。现在感觉学习它们理解思想只是掌握了百分之三十吧,剩下的还得需要实现了才能说这个数据结构我真的会了,路漫漫其修远兮。。。
附上参考和用到的知识的一些链接,希望对你有帮助

c++优先队列(priority_queue)用法详解

C++ greater()和less()

这个说了一下其在堆和排序中的不同
C++STL中的greater()和less()

#include <iostream>
#include <queue>
#include <algorithm> 
using namespace std;

int main(){
	int n;
	cin >> n;
	int num;
	priority_queue<int,vector<int>,greater<int>> qe;
	for(int i = 0;i<n;i++){
		cin >> num;
		qe.push(num);
	}
	int sum = 0;
	while(qe.size()>=2){
		int a = qe.top();
		qe.pop();
		int b = qe.top();
		qe.pop();
		sum += (a+b);
		qe.push(a+b);
	}
	cout << sum;
	return 0;
}

方式二:

emmm,参考网上代码并做出修改,使用堆,插入时向上调整,删除时向下调整,使其永远满足最小堆,详见代码注释
#include <iostream>
using namespace std;

int h[10001];
int n;
//交换,传入的是编号,从1开始
void swap(int x,int y){
	int tmp = h[x];
	h[x] = h[y];
	h[y] = tmp;
}
//向上调整函数,用于插入
void up(int i){
	bool flag = false;//是否继续调整
	if(i==1)
		return;
	while(i!=1&&!flag){//如果个数大于1且需要调整
		if(h[i]<h[i/2])//如果此编号的值小于父结点,就交换
			swap(i,i/2);
		else
			flag = true;//当前结点的值比父结点大,不再调整
		i /= 2;//更新编号,如果继续调整,则应该从它的父结点开始调整
	}
}
//向下调整,用于删除
void down(int i) {
	int t;
	bool flag = false;//作用同up函数
	while(i*2<=n&&!flag){//如果有左儿子且需要调整
		if(h[i]>h[i*2])//左儿子更小
			t = i*2;
		else
			t = i;
		if(2*i+1<=n){//如果有右儿子
			if(h[t]>h[i*2+1])//右儿子更小
				t = 2*i+1;
		}
		if(t!=i){//变化了,找到最终需要交换的了
			swap(t,i);
			i = t;//更新编号,用于下一次调整
		}else{
			flag = true;
		}
	}
}
//获取堆顶元素值,即取最小值
int getMin() {
	int tmp;//保存当前最小值
	tmp = h[1];
	h[1] = h[n];//将最大值与堆顶值互换,用于将删除最小值之后的堆变为最小堆
	n--;//删除后,元素个数减一
	down(1);//由于当前堆顶元素是最大值,需要向下调整至符合最小堆,编号为1
	return tmp;//返回调整前的堆顶值
}
int main() {
	cin >> n;
	int i;
	int sum = 0;
	for(i = 1;i<=n;i++) {
		cin >> h[i];
		up(i);//每插入一个数就向上调整
	} 
	while(n!=1) {//只有一个值时就是最终的sum值,因为一个值无法再取出然后再相加再up
		int a = getMin();
		int b = getMin();
		n++;//插入时个数+1
		h[n] = a + b;
		sum += h[n];
		up(n);
	}
	cout << sum;
	return 0;
}

下边是测试点,在PTA上有些题目集中有的可以找到原题,有时候不知道哪里错了可以在上边提交一遍找测试点,有的有,有的没有

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45845039/article/details/108073942
今日推荐