如其专题,这就是一道贪心题,至于怎么贪呢?
对了,本人菜鸡一个,如有错误,还请各位指正。
只要每次取最小的两个果堆,再加起来就行。
你可能会想到用强大的sort(),但是好像在这里会tle。
然后我就试着用之前新学的手写的堆排序,就愉快的ac了。
至于不清楚堆的童鞋,推荐一个博客给你们。(我就是这里学的哟)
废话不多说了了,直接上代码了。
#include<bits/stdc++.h>
using namespace std;
int n,a[10200];
void change(int x,int y)//定义一个交换堆中两个元素值的函数
{
int t=a[x];
a[x]=a[y];
a[y]=t;
return;
}
//向上调整函数
void siftup(int i)//传入一个需要向上调整的节点编号
{
int flag=0;//flag代表是否还需要向下调整
if(i==1) return;//如果是堆顶,就返回,无需调整
while(i!=1&&!flag)//不是堆顶并且i节点的值比父节点小就向上调整
{
if(a[i]<a[i/2])//判断是否小于父节点
change(i,i/2);//交换
else
flag=1;//当前节点比父节点大了就无需调整
i=i/2;//更新i的编号
}
}
//向下调整函数
void siftdown(int i)//传入一个需要向下调整的节点编号i,即从编号为i的点向下调整
{
int flag=0,t;//flag代表是否还需要向下调整
while(i*2<=n&&!flag)//只要i节点有左儿子,并且需要继续向下调整就执行
{
if(a[i*2]>a[i])//判断与左儿子大小,t记录较小节点编号
t=i;
else
t=i*2;
if(i*2+1<=n)//如果它还有右儿子,就继续比较,也记录小的节点的编号
{
if(a[t]>a[i*2+1])
t=i*2+1;
}
if(t!=i)//如果最小节点编号不是自己,说明子节点有比自己还小的
{
change(t,i);
i=t;//更新i为与它交换节点的编号。
}
else
flag=1;//否则说明当前节点已经比他的子节点小,无需调整
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=n/2;i>=1;i--)//建立小根堆
{
siftdown(i);
}
int num=n,ans=0;//num记录果子的堆数,ans记录合并后消耗的体力值
while(n>=2)//只要还能合并就继续
{
//把最小的两堆果子拿出,合并后再放回堆中。
int x=a[1];
a[1]=a[n];//最大的一果堆放到堆顶后调整其位置
n--;//把最小的一果堆替换掉后,果堆的数目-1;
siftdown(1);
int y=a[1];
a[1]=a[n];
n--;
siftdown(1);
a[++n]=x+y;
siftup(n);//插入一个合并后的果堆
ans+=(x+y);
}
printf("%d",ans);
return 0;
}