题解-合并果子(优先队列)

题目链接:

洛谷
信奥一本通

题目描述:

给定n个叶结点,每个结点有一个权值 W [ i ] W[i] W[i],将它们中两个、两个合并为树,假设每个结点从根到它的距离是 D [ i ] D[i] D[i],使得最终 ∑ ( w i ∗ d i ) ∑(wi * di) (widi)最小。

题解:

Part 1 合并果子

这道题有很多方法能做,我一开始竟然脑抽用了个双向队列???
然后用sort排序,果然T了
题解-合并果子(优先队列)_第1张图片
然而玄学的是,加了O2以后…
题解-合并果子(优先队列)_第2张图片

好吧进入正题
这题的做法很简单,就是经典的Huffman树,即每次选择权值最小的两个数相加,删去这两个数,保留它们的和,然后重新排序一遍,直到只剩下一个数

删去两个数,再插入它们的和没有什么难度,用普通的队列都能实现

但怎么排序呢?

这就是这道题的重点

用sort都会超时,那看来就只能用更高级的排序了
比如优先队列、multiset

Part 2 priority_queue和multiset

优先队列的头文件是#include
声明一个优先队列:

priority_queue <int>  q; //从大到小排序
priority_queue<int,vector<int>less<int> >q;  //从大到小排序
priority_queue<int,vector<int>,greater<int> >q;  //从小到大排序

显然,前两个的含义是一样的

操作基本和普通的队列一样

q.size();  //队列大小
q.empty(); //队列是否为空
q.push(a); //在队尾插入a
q.pop();   //删去队首元素
q.top();  //返回队首元素

multiset的作用和priority_queue差不多,也是自动排序
不过它的头文件是#include

Part 3 代码

优先队列:

#include 
using namespace std;
int a[10010];
int main(){
	priority_queue<int,vector<int>,greater<int> >q;
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
		q.push(a[i]);
	}
	int ans=0,x;
	while(q.size()>1){
		x=q.top();
		q.pop();
		x+=q.top();
		q.pop();
		q.push(x);
		ans+=x;
	}
	cout<<ans;
}

multiset:

#include
#include
using namespace std;

const int maxn=1e5+10;

multiset<int>st;

int main()
{
	int n,x;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>x;
		st.insert(x);
	}
	int ans=0;
	while(st.size()>1)
	{
		int a=*st.begin();
		st.erase(st.begin());
		
		int b=*st.begin();
		st.erase(st.begin());
		
		ans+=(a+b);
		st.insert(a+b);
	}
	cout<<ans<<endl;
	return 0;
}

创作时间:

2019-8-23

你可能感兴趣的:(2019暑,Hello,World)