Clever_Jimmy

导航

 

   0.简介:

     堆是一种神奇的数据结构,它可以支持以下几种操作:

    • 查询最大(最小)的元素
    • 删除最大(最小)的元素
    • 插入一个元素

     例如,这就是一个小根堆

                        

     其实堆就是一棵满足任意一个子节点都不大于(或不小于)其父节点的完全二叉树。

 1.基本操作:

    • 插入

      现在我们要插入一个新的元素8,那就先把它排到堆尾

                           

      然后我们把它上调直至满足堆的性质。先比较它的父节点,发现8<10,那么就把它与它的父节点交换。

                         

      再比较此时的父节点,发现8>=1,无法再向上调整。break退出循环。

 

      于是堆最终变成了这样:

                    

 

    • 弹出

      我们假设不想要这个1了,该怎么办呢?肯定是不能直接将它删除的。因为那样会破坏堆的性质。

      我们可以考虑这样:先把堆顶与堆尾交换,然后删除堆尾。

      再逐步把此时的堆顶(即原来的堆尾)向下调整至合适的位置,使得此时的堆满足堆的性质。

                       

      此时我们发现,5<10,但是8也<10啊!那怎么办呢?

      我们应先比较子节点的大小,选择小的那个与之交换。为什么?

      试想我们若把10与8交换,会出现问题:此时8与5不满足堆的性质了!我们该如何调整呢?

                        

      此时我们就应该选择小的那个,这样就没有问题了。

                          

      此时我们再比较此时节点与其子节点的大小,发现较小的那个都要比此时节点要大,那么向下调整至此结束。break跳出循环。

 

    • 查询最大(最小)元素

      这个就较为简单了。由于我们前面两种操作已经维护了堆的基本性质,我们现在返回堆顶元素即可。

   2.代码实现

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #define maxn 1000010
 4 #define rgi register int
 5 #define il inline
 6 #define swap(a,b) a^=b^=a^=b
 7 
 8 using namespace std;
 9 
10 int n, siz;
11 int h[maxn];//小根堆
12 
13 il void push(int val)
14 {
15     h[++siz] = val;//往队尾添加元素
16     int cur = siz;//cur表示当前要向上调整的节点编号,初始化为队尾编号
17     while(cur >= 1)//当cur不是堆顶的时候循环
18     {
19         if(h[cur]<h[cur >> 1])//如果自己的父亲比自己大
20             swap(h[cur], h[cur >> 1]);//交换
21         else
22             break;//如果无法交换,则表明已经调整完毕
23         cur >>= 1;//开始处理自己的父亲节点
24     }
25 }
26 
27 il void pop()
28 {
29     swap(h[1], h[siz]);//交换队首与队尾元素
30     h[siz--] = 0;//删除
31     int cur = 1;//cur表示当前要向下调整的节点编号,初始化为队首编号
32     while((cur << 1) <= siz)
33     {
34         int lc = cur << 1, rc = lc + 1, _next;
35         //lc是左儿子编号,rc是右儿子编号,_next是下一步要处理的节点编号(要么是左儿子,要么是右儿子)
36         if(rc <= siz)//如果rc没有超siz,就比较lc与rc的大小,交换小的那一个
37         {
38             if(h[lc] <= h[rc])
39                 _next = lc;
40             else
41                 _next = rc;
42         }
43         else//如果rc超了siz,那么只能考虑左儿子了
44             _next = lc;
45         if(h[_next] < h[cur])//如果下一步要处理的节点比当前节点小
46         {
47             swap(h[_next], h[cur]);//交换
48             cur = _next;//开始处理下一个节点
49         }
50         else
51             break;//如果无法交换,则表明已经调整完毕
52     }
53 }
54 
55 il int top()
56 {
57     return h[1];//堆顶自然是h[1]
58 }
59 
60 int main()
61 {
62     scanf("%d", &n);
63     for(rgi i = 1; i <= n; i++)
64     {
65         int op;
66         scanf("%d", &op);
67         if(op == 1)
68         {
69             int v;
70             scanf("%d", &v);
71             push(v);
72         }
73         else if(op == 2)
74             printf("%d\n", top());
75         else if(op == 3)
76             pop();
77     }
78     return 0;
79 }
80  

 

  

   3.应用

    洛谷P1090 合并果子

       考虑维护一个小根堆,每次弹出第一大与第二大的,然后合并(即加起来)之后再插入堆中。

       正确性不难证明:设有a<b<c,我们先考虑从小到大进行合并,则:

         $s_{1} = (a+b)+(c+a+b)=2a+2b+c $

         而其他的方法,如:

          $s_{2} = (b+c)+(a+b+c)=a+2b+2c $

         则一定有$s_{1} \leq s_{2}$

    洛谷P2085 最小函数值

       考虑不断维护一个小根堆,每次弹出最小的$ f(x) $,并将$ f(x+1) $ 压入堆里

 

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <cctype>
  5 #define rgi register int
  6 #define il inline
  7 #define ll long long
  8 #define maxn 10010
  9 
 10 using namespace std;
 11 
 12 int n, m, siz;
 13 int a[maxn], b[maxn], c[maxn];
 14 
 15 il int read()
 16 {
 17     rgi x = 0, f = 0, ch;
 18     while(!isdigit(ch = getchar())) f |= ch == '-';
 19     while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
 20     return f ? -x : x;
 21 }
 22 
 23 il void write(int x)
 24 {
 25     if(x < 0)    putchar('-'), x = -x;
 26     if(x > 9)    write(x / 10);
 27     putchar(x % 10 + 48);
 28 }
 29 
 30 struct node
 31 {
 32     int a, b, c;
 33     int val, x;
 34 } h[maxn];
 35 
 36 int f(struct node cur)
 37 {
 38     return cur.a * cur.x * cur.x + cur.b * cur.x + cur.c;
 39 }//求f(x)
 40 
 41 struct node top()
 42 {
 43     return h[1];
 44 }
 45 
 46 void push(struct node now)
 47 {
 48     h[++siz].val = now.val;
 49     h[siz].x = now.x;
 50     h[siz].a = now.a;
 51     h[siz].b = now.b;
 52     h[siz].c = now.c;
 53     int cur = siz;
 54     while(cur >= 1)
 55     {
 56         if(h[cur].val < h[cur >> 1].val)
 57             swap(h[cur >> 1], h[cur]);
 58         else
 59             break;
 60         cur >>= 1;
 61     }
 62 }
 63 
 64 void pop()
 65 {
 66     swap(h[1], h[siz--]);
 67     int cur = 1;
 68     while((cur << 1) <= siz)
 69     {
 70         int lc = cur << 1, rc = lc + 1, nxt;
 71         if(rc <= siz)
 72         {
 73             if(h[lc].val <= h[rc].val)
 74                 nxt = lc;
 75             else
 76                 nxt = rc;
 77         }
 78         else
 79             nxt = lc;
 80         if(h[nxt].val < h[cur].val)
 81         {
 82             swap(h[nxt], h[cur]);
 83             cur = nxt;
 84         }
 85         else
 86             break;
 87     }
 88 }
 89 
 90 int main()
 91 {
 92     n = read(), m = read();
 93     for(rgi i = 1; i <= n; ++i)
 94     {
 95         struct node temp;
 96         temp.a = read();
 97         temp.b = read();
 98         temp.c = read();
 99         temp.x = 1;
100         temp.val = f(temp);
101         push(temp);//初始化
102     }
103     for(rgi i = 1; i <= m; ++i)
104     {
105         struct node cur = top();
106         pop();
107         write(cur.val), putchar(' ');//提取堆顶的f(x)并输出
108         cur.x++;
109         cur.val = f(cur);
110         push(cur);//f(x+1)入堆
111     }
112     return 0;
113 }

 

posted on 2019-03-09 21:30  Clever_Jimmy  阅读(258)  评论(0编辑  收藏  举报