洛谷P1090-合并果子-题解

合并果子

Hello 大家好,我是CSDN新来的一名……小小的OI选手,刚开通了博客,比较激动233,所以先献上一次题解
那么下面切入正题(为什么不能开头空两个郁闷)


首先献上题目地址

https://www.luogu.org/problem/show?pid=1090

就是这么一道题了,目(keng)测(ding)很多大神都写过,不喜勿喷~


题目分析

很明显的贪心了,每次选两个最小的拿出来求和累加在放回去,直到只剩一堆为止
然后估计所有人一开始看到都是这样想的(手写的有语法错误请谅解哈)

for (int i=1;i<=n;i++) scanf("%d",&a[i]);
do
{ sort(a,a+1+n,cmp);
  ans+=(a[n]+a[n-1]);
  a[n-1]+=a[n];
  n--;
}while (n>1)

然后一看n到10000 我就(zhi zhang)了,于是就引入了神奇的someting



大神肯定司空见惯了,如果有新手的话还是略微解释一下
一棵二叉树,保证根节点一定最优,其两个孩子次优,以此类推
因为是二叉树所以更改和删除都是logn的。

大根堆就是根节点最大,反之亦然


再回到这题,容易发现,可以把n个数建成一个小跟堆,每次用logn的时间取出2个最小值,累加到ans里面,再合并放回去
要做n-1次嘛,每次logn,故复杂度为O(nlogn),也就过了,毕竟1W还是很小的,那么下面献上程序咯


#include 
#include 
#include 
using namespace std;
int f[10001];
int ans,len,n;
int get()
{
    return f[1];
}
void remove()
{
    int son,next;
    f[1]=f[len--];
    son=1;
    while (son*2<=len)
        {
        next=son*2;
        if (nextnext+1]next]) next++;
        if (f[son]next])break;
        swap(f[next],f[son]);
        son=next;
        }

}
void put(int x)
{
    int son,next;
    f[++len]=x;
    son=len;
    while (son>1)
    {
    next=son>>1;
    if (f[son]>=f[next]) break;
    swap(f[son],f[next]);
    son=next;
    }
}
int main()
{   
    len=0;
    scanf("%d",&n);
    int x;
    memset(f,0,sizeof(f));
    for (int i=1;i<=n;i++)
    {
    scanf("%d",&x);
    put(x);
    }
ans=0;
while (len>1)
{
    int t1=get();
    remove();
    int t2=get();
    remove();
    int t3=t1+t2;
    //printf("%d %d %d\n",t1,t2,t3);
    put(t3);
    ans+=t3;
}
printf("%d",ans);
return 0;
}

这道题也就过了,然而是手写堆,比较烦,容易错什么的,于是就有了stl库的优先队列这种interesting的东西,就是满足出队的总是最优,所以就可以写优先队列了,用上stl库,加个头文件

#include 

具体用法抱歉本蒟蒻还没用用过,用过给大家下一题的时候补上

**

谢谢

**

你可能感兴趣的:(题解)