POJ 3784 Running Median (动态中位数)

题目链接: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\n", 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\n", 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\n", 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\n", 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;
}

时间: 2024-10-15 23:39:57

POJ 3784 Running Median (动态中位数)的相关文章

POJ 3784 Running Median 动态求中位数 堆

题意. 1000个case 每个case 输入若干个数,对第k个输入,如果k为奇数,则输出前k个数的中位数 那么这就是动态求中位数了 实现的思路也比较简洁 用两个堆, 大顶堆和小顶堆 每次输入一个数,如果这个数比当前的中位数大,就存入小顶堆中,  否则就存入大顶堆. 然后调整, 小顶堆元素的个数要等于大顶堆的元素个数,或者比其多1. 如果小顶堆的元素太多,就塞到大顶堆里,反之亦然 这样一来就会发现.小顶堆的元素比所有大顶堆的元素都大, 而且小顶堆的堆顶就是中位数. 那么怎么样才能想到这样一个思路

HDU 3282 Running Median 动态中位数,可惜数据范围太小

Running Median Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=3282 Description For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read

POJ 3784 Running Median(动态维护中位数)

Description For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output the median (middle value) of the elements received so far. Input The first line of input contains

POJ 3784 - Running Median(动态中位数) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=3784 题目大意: 依次给出n个数字,求在数据输入过程中的所有中位数.(共有(n+1)/2个) 输入格式: 输入一个数字P(1<=P<=1000),表示数据组数. 对于每组数据,第一行输入数据组号和数字总个数(1<=个数<=9999),中间以一个空格隔开. 接下来每行给出至多10个数字. 输出格式: 对于每组数据,第一行输出数据组号和中位数个数,中间

POJ 3784 Running Median (最大最小堆)

最大最小堆动态求中位数 题意:输入M个数,当已输入的个数为奇数个时输出此时的中位数. 一共有M/2+1个中位数要输出,每一行10个. 分析: 用两个优先队列来模拟最大最小堆.中位数是x,就是有一半数比x小,一半数比x大. 刚好符合堆的特点. 用一个从大到小排序的优先队列q1来模拟小于x的数. 从小到大排序的优先队列q2来模拟大于x的数. 动态维护两个优先队列的元素个数.q1.size()=q2.size() 输入的数为偶数个时, q1.size()=q2.size()+1 输入的数为奇数个时.

POJ 3784 Running Median

Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1670   Accepted: 823 Description For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output the median (midd

poj 3784 用堆动态求解中位数

堆真是一种简单而又神奇的数据结构,以前用它求过前kth的数,现在又可以用两个堆来动态求解中位数. 算法: 构建一个大顶堆和一个小顶堆,分别记为g和l. 假设当前中位数为mid,新读入一个数为tmp,则: 1.如果tmp < mid,则将tmp插入大顶堆,跳到步骤3. 2.如果tmp >= mid,则将tmp插入小顶堆,跳到步骤4. 3.如果大顶堆的元素个数比小顶堆多2(两个堆个数不平衡),则将mid插入小顶堆,弹出大顶堆堆顶元素为新的mid. 4.与步骤3相反,如果小顶堆的元素个数比大顶堆多2

hdu 3282 Running Median

题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=3282 Running Median Description For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output the median (middle value) of th

【POJ3784】Running Median

Running Median Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3406   Accepted: 1576 Description For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output