题解 P1090 【合并果子】

本文详细解析洛谷P1090【合并果子】题目,通过贪心策略每次选取最小两堆果子合并,使用手写堆排序优化,避免TLE,分享完整代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷:题解 P1090 【合并果子】

如其专题,这就是一道贪心题,至于怎么贪呢?

对了,本人菜鸡一个,如有错误,还请各位指正。

只要每次取最小的两个果堆,再加起来就行。
你可能会想到用强大的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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值