HDU 4006 The kth great number 堆/优先级队列/线段树

来源:互联网 发布:软考初级程序员难吗 编辑:程序博客网 时间:2024/06/06 00:50

题意:先输入两个整数 n, k。然后有 n 行输入, I 代表写下一个数字, Q代表询问在所有写下的数字中第 k 大的数数多少?
题解:因为当写下的数少于 k 个是不存在询问的情况。所以可以建一个堆,然后没写下一个数字,若它比堆中的最小数字还小,则堆不做改变。若它比堆中的最小数字要大,那么将堆中的最小数字去掉,并加入刚写入的数字。
具体写了三种方法:

方法一:最小堆

#include <algorithm>#include <iostream>using namespace std;struct min_Heap  {      int array[1000002], size;    void siftDown ( int start ) //下滑调整,若子女的值小于父节点的值,则子节点上浮,之后继续向下层比较      {          int i = start, j = i * 2;          int temp = array[start];          while ( j <= size )          {              if ( j < size && array[j] > array[j+1] ) ++j;              if ( temp <= array[j] ) break;              else { swap(array[i],array[j]); i = j; j = j * 2; }          }      }    void siftUp ( int start ) //上滑调整,若子女的值小于父节点的值则互相交换      {          int j = start, i = j / 2;          int temp = array[j];          while ( j >= 1 )          {              if ( temp >= array[i] ) break;              else { swap(array[i],array[j]); j = i; i = i / 2; }          }      }    void build_minHeap () //建堆      {          int current = size / 2;          while ( current >= 1 )          {              siftDown ( current );              current--;          }      }} minHeap; int main(){char oper[3];int n, k, num;while ( scanf("%d%d",&n,&k) != EOF ){minHeap.size = 0;for ( int i = 1; i <= n; i++ ){scanf("%s",oper);if ( oper[0] == 'I' ){scanf("%d",&num);if ( minHeap.size < k ){minHeap.size++;minHeap.array[minHeap.size] = num;if ( minHeap.size == k )    minHeap.build_minHeap ();}else if ( num > minHeap.array[1] ){minHeap.array[1] = num;minHeap.siftDown ( 1 );}}elseprintf("%d\n",minHeap.array[1]);}}return 0;}


方法二:优先级队列。其实和上面一种方法没有本质区别
#include <queue>#include <iostream>using namespace std;int main(){char oper[3];int n, k, num;while ( scanf("%d%d",&n,&k) != EOF ){priority_queue< int,vector<int>,greater<int> > que;for ( int i = 1; i <= n; i++ ){scanf("%s",oper);if ( oper[0] == 'I' ){scanf("%d",&num);if ( que.size() < k )que.push(num);else{if ( num > que.top() ){que.pop();que.push(num);}}}elseprintf("%d\n",que.top() );}}return 0;}


方法三:线段树实现。线段树的实现效率虽然比以上两种略差,但是实用性很强。更容易拓展。

#include <algorithm>#include <iostream>using namespace std;#define L(u) ( u << 1 )#define R(u) ( u << 1 | 1 )#define N 1000001struct item{int l, r, v;} node[N*3];void build ( int u, int l, int r ){node[u].l = l;node[u].r = r;node[u].v = 0;if ( l == r ) return;int mid = ( l + r ) >> 1;build ( L(u), l, mid );build ( R(u), mid+1, r);}void update ( int u, int pos ){node[u].v++;if ( node[u].l == pos && node[u].r == pos )return;int mid = ( node[u].l + node[u].r ) >> 1;if ( pos <= mid )update ( L(u), pos );elseupdate ( R(u), pos );}int query ( int u, int k ){if ( node[u].l == node[u].r )return node[u].l;if ( k <= node[L(u)].v )return query ( L(u), k );elsereturn query ( R(u), k - node[L(u)].v );}int main(){char oper[3];int n, k, num, cnt;while ( scanf("%d%d",&n,&k) != EOF ){cnt = 0;build ( 1, 1, 1000000 );for ( int i = 1; i <= n; i++ ){scanf("%s",oper);if ( oper[0] == 'I' ){scanf("%d",&num);cnt++; /* cnt 统计一共写下的数字 */update ( 1, num );}else printf("%d\n", query(1,cnt-k+1) ); /* 注意是 cnt-k+1 */}}return 0;}



原创粉丝点击