优先队列和堆
能够完成下列操作的数据结构叫做优先队列
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 }
看下之前用贪心法解的一道题 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 }
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 }