题目链接:
题目描述:
给定n个叶结点,每个结点有一个权值 W [ i ] W[i] W[i],将它们中两个、两个合并为树,假设每个结点从根到它的距离是 D [ i ] D[i] D[i],使得最终 ∑ ( w i ∗ d i ) ∑(wi * di) ∑(wi∗di)最小。
题解:
Part 1 合并果子
这道题有很多方法能做,我一开始竟然脑抽用了个双向队列???
然后用sort排序,果然T了
然而玄学的是,加了O2以后…
好吧进入正题
这题的做法很简单,就是经典的Huffman树,即每次选择权值最小的两个数相加,删去这两个数,保留它们的和,然后重新排序一遍,直到只剩下一个数
删去两个数,再插入它们的和没有什么难度,用普通的队列都能实现
但怎么排序呢?
这就是这道题的重点
用sort都会超时,那看来就只能用更高级的排序了
比如优先队列、multiset
Part 2 priority_queue和multiset
优先队列的头文件是#include <queue>
声明一个优先队列:
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 <set>
Part 3 代码
优先队列:
#include <bits/stdc++.h>
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<iostream>
#include<set>
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