题目链接:http://poj.org/problem?id=3784
题目大意:依次输入n个数,每当输入奇数个数的时候,求出当前序列的中位数(排好序的中位数)。
此题可用各种方法求解。
排序二叉树方法,每个结点保存以其为根的左右子树中数的个数。如果数据出的够严格,这种方法会被卡的,除非是通过动态调整维持树的高度较小。
排序二叉树的代码如下:
#include <cstdio> using namespace std; #define N 20000 struct Node { int v; Node *lc, *rc;//左子树、右子树 int ln, rn;//左右子树下保存数的个数 }; Node* Tr;//树根 void insert(Node *&T, int v)//注意T要引用 { if(T==NULL) { T = new Node(); T->v = v; T->lc = T->rc = NULL; T->ln = T->rn = 0; return; } if(v <= T->v) { insert(T->lc, v); T->ln++; } else { insert(T->rc, v); T->rn++; } } int query(Node *T, int c) { int num = T->ln; if(c == num+1) return T->v; if(c < num+1) return query(T->lc, c); else return query(T->rc, c-num-1); } int ans[N]; int main() { int T, cas, n, v; scanf("%d", &T); while(T--) { int c = 0; Tr = NULL; scanf("%d %d", &cas, &n); for(int i = 0; i < n; i++) { scanf("%d", &v); insert(Tr, v); if(i % 2 == 0) ans[c++] = query(Tr, (i+2)/2); } printf("%d %d ", cas, (n+1)/2); printf("%d",ans[0]); for(int i = 1; i < c; i++) { if(i%10==0)puts(""); else printf(" "); printf("%d", ans[i]); } if(c % 10 != 0) puts(""); } return 0; }
堆方法,维护两个堆,一个大顶堆,一个小顶堆,大顶堆保存较小的一半数,小顶堆保存较大的一半数,任何时刻,任何时刻,保证大顶堆中元素的个数比小顶堆中的元素个数多1或相等,则大顶堆的堆顶元素就是中位数,代码如下:
#include <cstdio> #include <algorithm> using namespace std; #define N 20000 int smaq[N], bigq[N]; bool cmp(int a, int b){ return a > b; } int ans[N]; int main() { int T, cas, n, v; scanf("%d", &T); while(T--) { int c = 0; scanf("%d %d", &cas, &n); int t1 = 0, t2 = 0; for(int i = 0; i < n; i++) { scanf("%d", &v); if(t1 == 0 || bigq[0] >= v) bigq[t1++] = v; else smaq[t2++] = v; push_heap(bigq, bigq+t1); push_heap(smaq, smaq+t2, cmp); if(t1-t2 == 2) { pop_heap(bigq, bigq+t1); smaq[t2++] = bigq[--t1]; push_heap(smaq, smaq+t2, cmp); } else if(t2-t1 == 1) { pop_heap(smaq, smaq+t2, cmp); bigq[t1++] = smaq[--t2]; push_heap(bigq, bigq+t1); } if(i % 2 == 0) ans[c++] = bigq[0]; } printf("%d %d ", cas, (n+1)/2); printf("%d",ans[0]); for(int i = 1; i < c; i++) { if(i%10==0)puts(""); else printf(" "); printf("%d", ans[i]); } if(c % 10 != 0) puts(""); } return 0; }
线段树方法,先将所有数据读入,做离散化,这样,每个数插入的时候直接插入到其应该去的位置上(排好序的下标),就可以用线段树了。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define N 20000 int tr[N<<2];//线段树 void insert(int id, int l, int r, int pos)//在一个位置插入 { tr[id]++; if(l == r) return ; int lc = id<<1, rc = (id<<1)|1, mid = (l+r)>>1; if(pos <= mid) insert(lc, l, mid, pos); else insert(rc, mid+1, r, pos); } int query(int id, int l, int r, int c)//查询第c个数 { if(l == r) return l; int lc = id<<1, rc = (id<<1)|1, mid = (l+r)>>1; if(tr[lc] >= c) return query(lc, l, mid, c); else return query(rc, mid+1, r, c-tr[lc]); } int ans[N]; int sq[N], sortsq[N]; int find(int l, int r, int v) { while(l <= r) { int mid = (l+r)>>1; if(v == sortsq[mid]) return mid; else if(v < sortsq[mid]) r = mid-1; else l = mid+1; } } int main() { int T, cas, n, v; scanf("%d", &T); while(T--) { memset(tr, 0, sizeof(tr)); int c = 0; scanf("%d %d", &cas, &n); for(int i = 0; i < n; i++) scanf("%d", &sq[i]); memcpy(sortsq, sq, sizeof(sq)); sort(sortsq, sortsq+n); //for(int i = 0; i < n; i++) printf("%d ", sortsq[i]); puts(""); for(int i = 0; i < n; i++) { int id = find(0, n-1, sq[i]); insert(1, 0, n-1, id); if(i%2 == 0) //printf("id = %d ", query(1, 0, n-1, (i+2)/2)); ans[c++] = sortsq[query(1, 0, n-1, (i+2)/2)]; } //for(int i = 1; i <= 2*n; i++)printf("%d ", tr[i]); puts(""); printf("%d %d ", cas, (n+1)/2); printf("%d",ans[0]); for(int i = 1; i < c; i++) { if(i%10==0)puts(""); else printf(" "); printf("%d", ans[i]); } if(c % 10 != 0) puts(""); } return 0; }