全部相加B811 |
试题描述
|
有 n 个正整数的集合 S,每次可以从 S 中删除两个数,然后把它们的和放回集合,直到剩下一个数。每次操作的开销等于删除的两个数之和,求最小总开销。所有数均小于 105。
|
输入
|
第一行仅包含一个正整数 n,第二行包含 n 个正整数,两两之间用一个空格分隔。
|
输出
|
一个数,表示最小的总开销。
|
输入示例
|
5 3 6 2 7 4
|
输出示例
|
49
|
其他说明
|
数据范围:1 <= n <= 5000 。
|
这道题可以说是最小堆的模板题,适合对于堆的初学者(比如我)。
堆是一种优先队列,适合插入元素,快速寻找最大最小值,排序等功能。
本题的要求就是找到两个最大元素,然后插入两数之和,然后向下调整就行了。直到队列中只剩一个数,程序结束。
下面呈上代码:

#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,a[1000005]; long long ans; void down(int x)//向下调整 { int t,flag=0; while(x*2<=n && flag==0)//如果当前点有左儿子 { if(a[x*2]<a[x]) t=x*2; else t=x; if(x*2+1<=n && a[x*2+1]<a[t]) t=x*2+1;//有右儿子的情况 if(t!=x) //如果它换过了 { swap(a[t],a[x]); x=t; } else flag=1;//两个儿子都比他小,不用换了 } return ; } void creat()//建堆 { for(int i=n/2;i>=1;i--)//除了叶节点(n/2个叶节点),每个点都向下调整就行了 down(i); } int de()//删除堆顶元素 { int t=a[1]; a[1]=a[n]; n--;//元素数量-1 down(1); return t; } void new(int b)//新数 { int t; n++; a[n]=b; t=n; while(t>1)//新数要向上调整 { if(a[t/2]>a[t]) swap(a[t/2],a[t]); t/=2; } } int main() { int i,num; cin>>n; num=n; for(i=1;i<=n;i++) scanf("%d",&a[i]); creat();//建堆 for(i=1;i<num;i++) { int a=de(),b=de();//删除最小的两个数 new(a+b);//将新数插入堆中 ans+=a+b; } printf("%d ",ans); return 0; }