zoukankan      html  css  js  c++  java
  • CF1360H Binary Median

    原题链接  https://www.luogu.com.cn/problem/CF1360H

    题目大意 

    题解 

    ① 最常见最暴力的做法:

    将读入的二进制转化为十进制,然后依次删除,删除过程中维护中位数;

    虽然思路简单,但是考虑到实际维护的过程中十分复杂,对代码能力要求较高;

    ② 稍微巧妙一点的做法:

    其实我们并不需要维护每个过程中的中位数是几,我们关心的只是全部删除后的中位数是几;

    而且题目中给出了数据的原始个数(2m)和删除个数(n),也就是说,我们能知道删除完后的这 2m - n 的中位数是第几个数(设是第 k 个数,根据规律可得 k = ( 2m - n + 1 ) / 2),那么删除完后我们就输出这个位置的数就好了;

    这就有了另一个思路:我们只需要在删除过程中维护哪个数变成了第 k 个数 。

    拿样例模拟一下:

    n = 3,m = 3

    删除的数为:(010)2 = 2,(001)2 = 1 ,(111)2 = 7

    删除前的所有数:

    通过计算可以知道 k = ( 23 - 3 + 1 ) / 2 = 3,那么我们就确定了删完 3 个数之后的数据中第 3 个数是中位数;  

    然后我们就死盯着第 3 个数不放,看哪个数在删除之后移到了第 3 个数的位置(红色的数字代表最后的中位数上的数);

    由于数据是从 0 开始的,所以第 k 个数的大小为 k-1 (令 mid = k-1)。

    删除 2:

    删除 1:

    删除 7:

    过程中我们可以发现一个规律:

    对于我们所要维护的中位数 mid 来说,如果删除的数比 mid 大,那么 mid 的值是不变的(参考上图删除 7 的过程);如果删除的数小于等于 mid,那么 mid 的值就变成了右边的那个数(以下称为“ 右移一次 ”),注意要跳过已经被删除了的数;

    这么看来,我们可以先将所需删除的数从大到小排序,对于其中的 cnt 个比 mid 大的数,删除了并不影响 mid 的值;对于其中的 n - cnt 个小于等于 mid 的数,每删除一个 mid 就要右移一次,总共需要右移 n - cnt 次;

    其实我们也可以只存比 mid 大的那些数,对于那些比 mid 小的数,我们只关心它有多少个;同时这样的话,我们也不必将所删除的数从大到小排序了,因为二分查找也可以从小到大排序啊qwq 。 

    Code:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int T,n,m,cnt;
    long long len,mid;
    long long delet[1500];
    bool read()              //读入单个数字 
    {
        char ch=getchar();
        while(ch!='0'&&ch!='1') ch=getchar();
        return ch^48;
    }
    int check(long long x)   //查找x是否在delet数组里 
    {
        return delet[lower_bound(delet+1,delet+1+cnt,x)-delet]==x;  //lower_bound偷懒qwq 
    }
    void work()
    {
        for(long long i=mid+1;i<=len;i++)
        {
            if(!check(i))    //判断i是否被删除 
            {
                mid=i;       //没有被删除就来当mid 
                return ;
            }
        }
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d",&n,&m);
            len=1ll<<m;                  //len是初始数的个数 
            mid=(len-n+1)/2-1;           //删除一些数之后的中位数的位置上的数现在是几 
            memset(delet,0,sizeof(delet));
            cnt=0;                       //记录删除的数中有多少个数比mid大 
            for(int i=1;i<=n;i++)        
            {
                long long a=len/2;   
                long long num=0;         //num是转化为十进制之后的数 
                for(int j=1;j<=m;j++)    //二进制转化为十进制 
                {
                    int x=read();
                    if(x) num+=a;
                    a/=2;
                }
                if(num>mid) delet[++cnt]=num;   //只需记录所有比mid大的数 
            }
            sort(delet+1,delet+1+cnt);   //从小到大排序,便于二分查找 
            for(int i=1;i<=n-cnt;i++)    //总共有n-cnt个数小于等于mid,就要右移n-cnt次 
                work();
            len/=2;    
            while(len)                   //十进制转化为二进制 
            {
                if(mid>=len) 
                {
                    printf("1");
                    mid-=len;
                }
                else printf("0");
                len/=2;
            }
            printf("
    ");
        }
        return 0;
    }

    特别鸣谢 一扶苏一 帮我 debug;

     

  • 相关阅读:
    inspector元素定位[wda-facebook]
    pip下载 requirements.txt
    APP运行出现闪退
    安卓的webview报错
    深度强化学习入门笔记
    强化学习入门笔记
    如何将本地代码上传到GitHub
    Sublime插件Emmet的错误及Tab补全代码
    flask的url错误
    计算输出特征图的空间尺寸
  • 原文地址:https://www.cnblogs.com/xcg123/p/13095407.html
Copyright © 2011-2022 走看看