zoukankan      html  css  js  c++  java
  • 【POJ 3784】Running Median【堆】【链表】

    题目大意:

    题目链接:http://poj.org/problem?id=3784
    给出由n个数字组成的的数字串,每当输入奇数个时,输出当前的中位数。


    思路:

    思路一:
    如果我们将这个数列从中位数分开,将比中位数小的数放入大根堆,比中位数大的数放入小根堆,中位数也放入大根堆,那么每次读入时,维护两个堆的情况,保持大根堆的元素不比小根堆的元素少且最多多出一个,那么每次输入为奇数个时,中位数为大根堆的对顶。
    时间复杂度O(nlogn)


    思路二:
    要求中位数,那么就要现将这个数组排序。那么就可以先得到最终的中位数。然后数据保持单调性,将一个数与左右用链表连接,并记录下原来在这个位置的是哪个数字。然后每次删除最后输入的两个数字a,b(保持数字个数为奇数),然后分类讨论:

    1. a>midb>midmid=p[mid].l
    2. a<misb<midmid=p[mid].r
    3. min(a,b)<midmax(a,b)>midmid不变
    4. mid(a,b)=midmax(a,b)>midmid=p[mid].l
    5. max(a,b)=midmid(a,b)<midmid=p[mid].r

    最终倒着输出即可。
    时间复杂度O(nlogn)


    代码:

    对顶堆(手打):

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    int n,m,a,maxn[12001],minn[12001],ans[12001],tot_max,tot_min;
    bool ok;
    
    void up_min(int x)  //小根堆上移操作
    {
        int y=x/2;
        while (y>0&&minn[y]>minn[x])  //能往上
        {
            swap(minn[y],minn[x]);  //上移
            x=y;
            y=x/2;
        }
    }
    
    void up_max(int x)  //大根堆上移操作
    {
        int y=x/2;
        while (y>0&&maxn[y]<maxn[x])
        {
            swap(maxn[y],maxn[x]);
            x=y;
            y=x/2;
        }
    }
    
    void down_min(int x)  //小根堆下移操作
    {
        int y=x*2;
        while ((y<=tot_min&&minn[y]<minn[x])||(y+1<=tot_min&&minn[y+1]<minn[x]))  //能继续下移
        {
            if (minn[y+1]<minn[y]&&y+1<=tot_min) y++;  //求更优解
            swap(minn[x],minn[y]);  //下移
            x=y;
            y=x*2;
        }
    }
    
    void down_max(int x)  //大根堆下移操作
    {
        int y=x*2;
        while ((y<=tot_max&&maxn[y]>maxn[x])||(y+1<=tot_max&&maxn[y+1]>maxn[x]))
        {
            if (maxn[y+1]>maxn[y]&&y+1<=tot_max) y++;
            swap(maxn[x],maxn[y]);
            x=y;
            y=x*2;
        }
    }
    
    void push_max()  //将小根堆的对顶插入大根堆
    {
        int a=minn[1];  //取出
        swap(minn[1],minn[tot_min]);
        minn[tot_min]=0;  
        tot_min--;  //删除
        down_min(1);  //维护
        maxn[++tot_max]=a;  //插入
        up_max(tot_max);  //维护
    }
    
    void push_min()  //将大根堆的对顶插入小根堆
    {
        int a=maxn[1];
        swap(maxn[1],maxn[tot_max]);
        maxn[tot_max]=0;
        tot_max--;
        down_max(1);
        minn[++tot_min]=a;
        up_min(tot_min);
    }
    
    int main()
    {
        scanf("%d",&m);
        for (int l=1;l<=m;l++)
        {
            scanf("%d",&n);
            scanf("%d",&n);
            printf("%d %d\n",l,n/2+1);
            memset(maxn,0,sizeof(maxn));
            memset(ans,0,sizeof(ans));
            memset(minn,0,sizeof(minn));
            tot_max=tot_min=0;  //初始化
            for (int i=1;i<=n;i++)
            {
                scanf("%d",&a);
                if (!tot_max)  //堆内没有元素
                 maxn[++tot_max]=a;
                else if (a>maxn[1])  
                {
                    minn[++tot_min]=a;
                    up_min(tot_min);
                }
                else
                {
                    maxn[++tot_max]=a;
                    up_max(tot_max);
                }
                while (tot_min>tot_max)  //维护两个堆
                {       
                    push_max();
                }
                while (tot_max>tot_min+1)
                {
                    push_min();
                }
                if (i%2)  //记录答案
                {
                    ans[i/2+1]=maxn[1];
                }
            }
            ok=true;
            for (int i=1;i<=n/2+1;i++)
            {
                if (i%10==1&&i!=1) 
                {
                    printf("\n");
                    ok=false;
                }
                else ok=true;
                printf("%d ",ans[i]);
            }
            if (ok) printf("\n");
        }
        return 0;
    }

    链表:

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    int m,n,ans[12001],mid,sum;
    bool ok;
    
    struct node1  //链表
    {
        int l,r,num;
    }p[12001];
    
    struct node2  //每个数字
    {
        int a,num,old;
    }a[12001];
    
    bool cmp(node2 x,node2 y)
    {
        return x.a<y.a;
    }
    
    void find()  //求出一个位置在没有排序之前的位置
    {
        for (int i=1;i<=n;i++)
         a[a[i].old].num=i;
    }
    
    void make()  //建链表
    {
        for (int i=1;i<n;i++)
        {
            p[i].r=i+1;
            p[i+1].l=i;  //将i和i+1连接
            p[i].num=i;  //记录编号
        }
        p[n].num=n; 
    }
    
    void Delete(int x)  //删除节点
    {
        p[p[x].l].r=p[x].r;
        p[p[x].r].l=p[x].l;
    }
    
    void play()  //求答案
    {
        int k=n;
        int one,two=0;
        while (k>0)
        {
            one=a[k--].num;
            two=a[k--].num;  //最后输入的两个数
            if (one<mid&&two<mid)
             mid=p[mid].r;
            else if (one>mid&&two>mid)
             mid=p[mid].l;
            else if ((one==mid&&two>mid)||(two==mid&&one>mid))
             mid=p[mid].l;
            else if ((one==mid&&two<mid)||(two==mid&&one<mid))
             mid=p[mid].r;
            Delete(one);
            Delete(two);  //删除
            ans[++sum]=a[mid].a;  //记录中位数
        }
    }
    
    void print()  //输出
    {
        for (int i=1;i<sum;i++)
        {
            if (i%10==1&&i>1) 
            {
                printf("\n");
                ok=false;
            }
            else ok=true;
            printf("%d ",ans[sum-i]);  //由于是倒着记录的,所以要倒过来
        } 
        printf("\n");
    }
    
    int main()
    {
        scanf("%d",&m);
        for (int l=1;l<=m;l++)
        {
            scanf("%d",&n);
            scanf("%d",&n);
            printf("%d %d\n",l,n/2+1); 
            memset(ans,0,sizeof(ans));
            sum=0;
            for (int i=1;i<=n;i++)
            {
                scanf("%d",&a[i].a);
                a[i].old=i;  //记录以前编号
            } 
            sort(a+1,a+1+n,cmp);  //排序
            mid=n/2+1;
            ans[++sum]=a[mid].a;
            find();
            make();         
            play();
            print();        
        }
        return 0;
    }
  • 相关阅读:
    Asp.Net Web API 2第八课——Web API 2中的属性路由
    Asp.Net Web API 2第七课——Web API异常处理
    Asp.Net Web API 2第六课——Web API路由和动作选择
    Asp.Net Web API 2第五课——Web API路由
    开始学习python
    BMI 小程序 购物车
    深浅copy 文件操作
    字典 dict 集合set
    基本数据类型 (str,int,bool,tuple,)
    python 运算符
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998727.html
Copyright © 2011-2022 走看看