• 动态中位数


    题意

    题意

    题解

    注:本文的代码中的输出不严格正确,反正loj的数据也没多强,不会PE。如果后面加强了当我没说

    做法1

    没错,第一个还是我的做法。

    记得是哪年的SCP还是NOIP初赛竟然程序题就是这个思路,然后我照搬了。

    我们这道题目反着考虑,它让我们加数,我们就删除数字,从后往前删除数字,我们先建一个排好序的链表,然后从中一个个删除数字,然后通过链表来跳,由于每次最多删除两个数字,除2就是一个,所以链表每次最多跳一次,时间复杂度:(O(nlogn))

    当然有一种情况要特殊考虑一下,就是当(n=2)时删除到了中位数的位置,这种情况要处理一下,其他也就没什么了。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define  N  11000
    using  namespace  std;
    struct  fuck
    {
    	int  x,y;
    }a[N];
    inline  bool  cmp(fuck  x,fuck  y){return  x.x<y.x;}
    struct  node
    {
    	int  l,r,x/*权值*/;
    }b[N];
    void  del(int  x)
    {
    	b[b[x].l].r=b[x].r;
    	b[b[x].r].l=b[x].l;
    }
    int  n,be[N];
    int  list[N],top;
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)
    	{
    		top=0;
    		int  t;scanf("%d%d",&t,&n);
    		for(int  i=1;i<=n;i++)
    		{
    			scanf("%d",&a[i].x);
    			a[i].y=i;
    			b[i].l=i-1;b[i].r=i+1;
    		}
    		sort(a+1,a+n+1,cmp);
    		for(int  i=1;i<=n;i++)be[a[i].y]=i,b[i].x=a[i].x;//确定其在链表中的位置 
    		int  ans=n/2+1,l=n/2/*左边有多少个数字*/,r=n/2/*右边有多少个数字*/;
    		for(int  i=n;i>=1;i--)//按顺序删除 
    		{
    			if(i%2==1)
    			{
    				if(l<r)
    				{
    					ans=b[ans].r;l++;r--;
    				}
    				else  if(l>r)
    				{
    					ans=b[ans].l;l--,r++;
    				}
    				list[++top]=b[ans].x;
    			}
    			if(be[i]==ans)//刚好卡在中位数的位置
    			{
    				if(l>r)ans=b[ans].l,l--,r++;
    				else  if(l<=r)ans=b[ans].r,l++,r--;
    			}
    			if(be[i]>ans)r--;
    			else  l--;
    			del(be[i]);
    		}
    		//后面就是输出的问题了。
    		printf("%d %d
    ",t,n/2+1);
    		int  now=0;
    		for(int  i=top;i>=1;i--)
    		{
    			now++;
    			if(now>10)
    			{
    				now-=10;
    				printf("
    ");
    			}
    			printf("%d",list[i]);
    			if(now!=10)printf(" ");
    		}
    		printf("
    ");
    	}
    	return  0;
    }
    

    做法2

    https://www.acwing.com/solution/content/838/

    搞两个堆,然后不断插入,如果(size)之差(≥2)了,时间复杂度:(O(nlogn))。(当然,两个堆的做法也可以有其他实现方法(例如像做法1一样,等到奇数再调节,而且最多调节一次),但本质上都是通过两个堆求中间的数字。)

    时间复杂度:(O(nlogn))

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <vector>
    using namespace std;
    struct cmp1
    {
        bool operator ()(int &a,int &b)
        {
            return a>b;//小根堆,不是大根堆
        }
    };
    priority_queue <int,vector<int>, cmp1> q1,kong1;
    priority_queue <int> q2,kong2;
    void init()
    {
        int t,x,n,now;
        cin>>t;
        while(t--)
        {
            cin>>x>>n;
            cout<<x<<" "<<(n+1)/2<<endl;
            q1=kong1;
            q2=kong2;
            int cnt=0;
            for (int i=1;i<=n;i++)
            {
                cin>>now;
                if(q1.empty())
                    q1.push(now);
                else
                {
                    if(now>q1.top()) 
                        q1.push(now);
                    else 
                        q2.push(now);
                    while(q1.size()<q2.size())
                    {
                        q1.push(q2.top());
                        q2.pop();
                    }
                    while(q1.size()>q2.size()+1)
                    {
                        q2.push(q1.top());
                        q1.pop();
                    }
                }
                if (i&1)
                {
                    cnt++;
                    cout<<q1.top()<<" ";
                    if (!(cnt%10))
                        cout<<endl;
                }
            }
            puts("");
        }
    }
    int main()
    {
        init();
        return 0;
    }
    
    
    作者:秦淮岸灯火阑珊已退役
    链接:https://www.acwing.com/solution/content/838/
    来源:AcWing
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    做法3

    我们考虑用线段树来找到中位数是哪个,对于第(i)次而言,我们就是要找到(frac{i}{2}+1)大的数组,那么能不能让这个这么大的数字自动找上门来呢?

    我们先让(t[i]=frac{i}{2}+1)

    我们排序一遍,然后从小到大看,如果这个数字(x)原本就是在第(i)次及之前插入的,我们就把(t[i]--),如果(t[i]=0)了,那么这个就是中位数。

    不难发现,如果一个数字是第(i)次插入的话,那么(t[i])~(t[n])都要减一。

    那么我们只要用线段树管理(t)数组不就行了吗?

    但是怎么处理(t[i]=0)的情况呢?

    我们暴力储存正数最小值,当正数最小值等于(0)时就暴力进入这个区域重复此操作,把为(0)的数拿出来同时更新最小值。

    时间复杂度:对于管理([l,r])区间的节点,他最多有(r-l+1)次进入机会,所以全部节点的范围加起来,就是(O(nlogn))啦。

    当然,这也能解决每一次插入求的不是中位数,而是第(k)大的题目。

    当然,由于这道题目只是当奇数的时候再查询,所以可以处理一下减少常数,嫌麻烦也可以直接全部偶数设为inf,这样也没有多大问题。

    没有代码。

    做法4

    平衡树他不香吗。(求(frac{i}{2}+1)大值)

    时间复杂度:(O(nlogn))

  • 相关阅读:
    k8s之docker被屏蔽后下载方法
    代码合并工具Beyond Compare的使用技巧
    (转)设计模式——观察者模式
    notepad问题汇总
    个人构建问题
    内部git常用总结
    (转)动态规划算法—买卖股票的最佳时机系列
    (转)java中引用传递和值传递
    互联网面试题
    (转)UML类图与类的关系详解
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13408490.html
Copyright © 2020-2023  润新知