优先队列和堆

能够完成下列操作的数据结构叫做优先队列

1.插入一个数值

2.取出最小的数值(获得数值并且删除)

能够使用二叉树高效地解决上述问题的,是一种叫做“堆”的数据结构。(二叉堆)

“堆”最重要的性质就是儿子的值一定不小于父亲的值。除此之外,树的节点是按照从上到下,从左到右的顺序紧凑排列的。

向堆中插入数值时,首先在堆的末尾插入该数值,然后不断向上提升直到没有大小颠倒为止。

从堆中删除最小值时,首先把堆的最后一个节点的数值复制到根节点上,并且删除最后一个几点。然后不断向下交换直到没有大小颠倒为止。

在向下交换的过程中,如果有2个儿子,那么选择数值较小的儿子(如果儿子比自己小)进行交换。

每个操作的复杂度为O(logn)

下面是堆的实现

 1 const int max_n=1001;
 2 int heap[max_n],sz=0;
 3 void push(int x)
 4 {
 5     //自己节点的编号
 6     int i=sz++;
 7     while(i>0)
 8     {
 9         //父亲节点的编号
10         int p=(i-1)/2;
11         //如果已经没有大小颠倒则退出
12         if(heap[p]>=x) break;
13         //把父亲节点的数值下放,自己上提
14         heap[i]=heap[p];
15         i=p;
16     }
17     heap[i]=x;
18 }
19 
20 int pop()
21 {
22     //最小值(树根)
23     int ret=heap[0];
24     //要提到根的数值
25     int x=heap[--sz];
26     //从根开始向下交换
27     int i=0;
28     while(i*2+1<sz)
29     {
30         //左儿子,右儿子
31         int a=i*2+1,b=i*2+2;
32         if(b<sz&&heap[a]>heap[b]) a=b;
33         //如果没有大小颠倒则推出
34         if(heap[a]>=x) break;
35         //把小儿子的数值提上来
36         heap[i]=heap[a];
37         i=a;
38     }
39     heap[i]=x;
40     return ret;
41 }
heap

看下之前用贪心法解的一道题   http://www.cnblogs.com/wangkaipeng/p/6425569.html

农夫约翰为了修理栅栏,要将一块很长的木板切割成n块。准备切成的木板的长度为l1,l2,...,ln。未切割前木板的长度恰好为切割后木板长度的总和。每次切断木板时,需要的开销为这块木板的长度。求按要求切完的最小开销。只说明了大概意思。

用优先队列实现如下。方便不少。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 #include<queue>
 5 #define INF 10000000
 6 using namespace std;
 7 const int max_n=1001;
 8 int ans=0;
 9 int L[max_n];
10 int n;
11 int main()
12 {
13     cin>>n;
14     //从小到大取出数值的优先级队列,可将greater改为less,即为从大到小
15     priority_queue<int,vector<int>,greater<int> > q;
16     for(int i=0;i<n;i++)
17     {
18         cin>>L[i];
19         q.push(L[i]);
20     }
21     while(q.size()>1)
22     {
23         //取出最短木板和次短木板
24         int l1=q.top();
25         q.pop();
26         int l2=q.top();
27         q.pop();
28 
29         //合并木板
30         int l=l1+l2;
31         ans+=l;
32         q.push(l);
33     }
34     cout<<ans<<endl;
35     return 0;
36 }
View Code

poj2431

为方便输入数据的加油站到终点的距离改为从起点到加油站的距离。

“可以认为在到达加油站i时,就获得了一次在之后的任何时候都可以加Bi单位汽油的权利”

因为希望到达终点时加油次数尽可能少,所以当燃料为0之后再进行加油。(选最大的Bi)。用优先队列实现。

 1 #include<bits/stdc++.h>
 2 #define INF 10000000
 3 using namespace std;
 4 const int max_n=1001;
 5 int ans=0;
 6 int A[max_n];
 7 int B[max_n];
 8 int n,l,p;
 9 int main()
10 {
11     priority_queue<int> q;
12     cin>>n>>l>>p;
13     for(int i=0;i<n;i++)
14     {
15         cin>>A[i];
16     }
17     for(int i=0;i<n;i++)
18     {
19         cin>>B[i];
20     }
21     //把终点也当成加油站
22     A[n]=l;
23     B[n]=0;
24     //记录当前位置
25     int pos=0;
26     for(int i=0;i<n;i++)
27     {
28         //达到下一个加油站的距离
29         int d=A[i]-pos;
30         //油量无法到达
31         while(p-d<0)
32         {
33             if(q.empty())
34             {
35                 puts("-1");
36                 return 0;
37             }
38             //选择一个油量最多的加油
39             p+=q.top();
40             q.pop();
41             ans++;
42         }
43         p-=d;
44         pos=A[i];
45         q.push(B[i]);
46     }
47     cout<<ans<<endl;
48     return 0;
49 }
View Code
原文地址:https://www.cnblogs.com/wangkaipeng/p/6433930.html