堆排序(优先队列)——合并果子

原创 2016年08月29日 22:01:27

首先说一下堆的性质:分为大根堆和小根堆,根节点都是最值,小根堆的根节点是最小的,每个堆都比它的两个子堆要小,大根堆的根节点是最大的,每个堆都比它的两个子堆要大。

顺便说一下二叉树的性质,左子树小于根节点小于右子树,所以都要先序遍历。

合并果子,是指有n堆果子,每次合并两堆,每次花费的力气为两堆之和,求合并为一堆后,花费的最小力气。

显然,每次合并最小的两堆即可满足题意。

又显然,要用堆排序,这里用小根堆。

(PS:堆跟二叉树一样,都是一个一维数组,每个点的左子树(左堆)的编号等于它*2,右子树(右堆)的编号等于它*2+1)

堆和树的优势:不需要像队列一样一个个枚举 而是每次判断将剩余情况二分,能将O(N)变成O(logN)。(二分的思路有很多 堆和树还有快排)

显然二分的前提是队列要有某种规律,比如需要先排好序。

比如一个数列1,2,3,4,5,6,7,8,9,10;(a[1]~a[10])

比如要查找8;

倘若枚举,显然要8次;

如果二分,一开始的范围显然是全部(1~10)。

取中间a[(1+10)/2]=a[5]=5<8;

那么范围变为6~10。(一次查找)

继续查找,在剩余范围二分。

a[(6+10)/2]=a[8]=8。找到目标(两次查找)

这就是二分的优点。

当然,倘若要寻找1,枚举会比二分快,但二分的优势是整体优化,也就是说,在刚刚的样例里,显然所有的数在4次以内都会被搜到,而枚举,最多需要10次。

下面上代码:

#include<iostream>
#include<cstdio>
using namespace std;
long long ans;
int a[110000],len,s;
int main(){
    int i,j,k;
    int n,u;
    cin>>n;
    for(i=1;i<=n;i++){
        cin>>a[i];//每次读入一个堆,存在队尾,因为直接在最低层,可能会比之前的数小,不满足堆的性质,就开始排序(往上浮)
        u=i;
        while(a[u]<a[u/2] && u>1){//如果它比它的根堆还小 并且它不是根堆(若它是根堆即a[1]那么它的父堆为a[0]=0 所以需要特判)
            k=a[u];
            a[u]=a[u/2];
            a[u/2]=k;
            u/=2;//交换位置 u指新插入的数在数组中的位置
        }
    }
    len=n;
    while(len>1){
        s=a[1];//每次取前两堆求和,先取出第一堆
        a[1]=a[len];//将第一堆直接赋值成最后一堆
        len--;//取出了一堆 所以长度-1
        u=1;//将最后一堆变成了第一堆 很可能不满足堆的性质 此时堆的编号为1 可能需要往下沉
        while((a[u*2]<a[u] && u*2<=len) || (a[u*2+1]<a[u] && u*2+1<=len)){//若它比它的子堆大 并且它的子堆未超出长度(之前删除的堆没有覆盖 只是将长度-1了所以数组后面的值仍然在那)
            if(a[u*2]>a[u*2+1] && len>=u*2+1)u=u*2+1;//优先往小的那边沉
            else u*=2;
            k=a[u];
            a[u]=a[u/2];
            a[u/2]=k;//交换
        }
        s+=a[1];//之前已经取出过一堆了 现在重新使堆满足性质后 取出第二堆相加
        a[1]=s;//将取出的两堆合起来放回去,显然此时可能不满足堆的性质了,需要往下沉
        ans+=s;//将花费的力气加入ans
        u=1;//此时可能需要下沉的堆的编号为1
        while((a[u*2]<a[u] && u*2<=len) || (a[u*2+1]<a[u] && u*2+1<=len)){
            if(a[u*2]>a[u*2+1] && len>=u*2+1)u=u*2+1;
            else u*=2;
            k=a[u];
            a[u]=a[u/2];
            a[u/2]=k;//交换
        }
    }
    cout<<ans;//输出
    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

合并类动态规划,石子归并,合并石子解题报告

设有N堆沙子排成一排,其编号为1,2,3,…,N(N
  • txl16211
  • txl16211
  • 2014年10月30日 19:47
  • 5873

石子合并问题--动态规划;贪心

石子合并问题石子合并问题是最经典的DP问题。首先它有如下3种题型: (1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆...
  • gatieme
  • gatieme
  • 2015年10月17日 20:18
  • 5503

NYOJ 石子合并(一)经典区间DP

石子合并(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的...
  • briup_acmer
  • briup_acmer
  • 2015年01月14日 10:41
  • 3104

【原创】堆排序+合并果子+优先队列

堆排序 今天讲“堆”,堆就是用数组表示完全二叉树。 堆有一个有趣的性质:一个编号为i的结点的父亲结点为编号i/2的结点,左儿子为2i,右儿子为2i+1。 人们还规定,如果每个数都大于等于自己的父结...
  • c20182030
  • c20182030
  • 2016年10月03日 09:54
  • 460

SDUT OJ 树-堆结构练习——合并果子之哈夫曼树 C++优先队列练习

今天做题跟同学学习了优先队列,瞬间感觉不错哦。就记下来了。。。以后复习用。。。。 题目描述  在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合...
  • xu12110501127
  • xu12110501127
  • 2013年06月28日 19:34
  • 3002

sdut oj2127 树-堆结构练习——合并果子之哈夫曼树(优先队列)

题目链接:点击打开链接 树-堆结构练习——合并果子之哈夫曼树 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^...
  • Annfan123
  • Annfan123
  • 2016年08月13日 21:29
  • 211

SDUT-2127- 树-堆结构练习——合并果子之哈夫曼树(STL 优先队列)

树-堆结构练习——合并果子之哈夫曼树 Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Subm...
  • u013476556
  • u013476556
  • 2014年08月04日 18:29
  • 547

树-堆结构练习——合并果子之哈夫曼树 优先队列

树-堆结构练习——合并果子之哈夫曼树 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  在一个果园里,多多已...
  • became_a_wolf
  • became_a_wolf
  • 2015年08月06日 21:10
  • 308

树-堆结构练习——合并果子之哈夫曼树 (STL 优先队列)

题目描述  在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重...
  • ZHC_AC
  • ZHC_AC
  • 2015年08月10日 10:48
  • 476

C++ STL第一次——果子合并(优先队列)

【问题描述】 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量...
  • jianing1996
  • jianing1996
  • 2012年12月12日 22:26
  • 370
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:堆排序(优先队列)——合并果子
举报原因:
原因补充:

(最多只允许输入30个字)