• 【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;
    }
  • 相关阅读:
    NXOpen测量柱面两条边的最近距离
    NXOpen打开当前部件所在目录
    NXOpen 一键移除参数
    NXOpen 特征、体、线、圆弧、点通过迭代器遍历
    NXOpen批量创建部件
    NXOpen 创建圆柱面中心线
    NXOpen创建基准座标
    NXOpen 通过迭代器获取工作部件的体、面、边、点
    NXOpen 通过部件迭代器获取已加载部件的全路径
    NXOpen 批量导出STP、IGS、DWG、DXF、PDF(五合一)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998727.html
Copyright © 2020-2023  润新知