蒟篛的P1334 瑞瑞的木板 题解(合并果子也是可以这样做的)

/*

本题是一个比较简单的堆排序的题目,

思想和之前的合并果子非常像(简直一模一样) 我每次找到数列中最小的俩个进行合并,

再将合并后的数存入数列中 (可以用堆实现,大佬可以无视直接用优先队列,原理是一样的,

我们建立一个二叉树,让每个子节点都大于它的父亲节点 二叉树的根节点就成了最小值)

再找新的数列中最小的俩个;

(以此类推,并将过程中找到的合并后的值加起来就是答案)

*/

#include<bits/stdc++.h>

using namespace std;

int n;

long long l,s[50000],da;//题目数字给的太大了,不开long long过不了。

void sf(int wz)//上浮,将一个新值按规矩上移(每个子节点都大于它的父亲节点)

{

  while(wz!=1)//到顶结束

  {

    int cnm=wz>>1;//位运算,相当于除以2(向下取);

    if(s[wz]<s[cnm])

       swap(s[wz],s[cnm]);

    else//找到自己位置也可以结束

      break;

     wz=cnm;

  }

}

void xc()//下沉,将一个较大值按规矩下移(顺便找到新的最小)

{

  int wz=1;

  while((wz<<1)<=n)

  {

    int cnm=wz<<1;//相当于乘2

     int t=wz;//t表示我当前的值和t的值交换

    if(cnm<=n&&s[wz]>s[cnm])

      t=cnm;//有左边并且可换

    if(s[wz]>s[cnm+1]&&s[cnm]>s[cnm+1]&&cnm+1<=n)

      t=cnm+1;//有右边并且比左边好

    if(t!=wz)//不等交换

      swap(s[wz],s[t]);

     else//找到位置,不动了

      ·break;

     wz=t;

  }

}

int main()

{

  cin>>n;

  for(int i=1;i<=n;i++)

  {

    cin>>l;

    s[i]=l;//表示我新进来一个数,我先暂时把它放在完全二叉树末尾(也就是第i个)

    sf(i);//我要把树中第i个点的数按要求上浮(上小下大)

  }

  for(int i=1;n!=1;i++)//当我节点个数为1时就做完了

  {

    int k=s[1];//取一个最小

    s[1]=s[n--];//将这个最小变为一个较大值

    xc();//把这个较大值下沉(相当于去掉最小,重新找最小)

    k=k+s[1];//再取

    s[1]=s[n--];//再删

    xc();

    s[++n]=k;//将新值存入完全二叉树末尾

    sf(n);//再次上浮

    da=da+k;//累计答案

  }

  cout<<da;

}

原文地址:https://www.cnblogs.com/ren-meng-hua-qi/p/9108637.html