• 二分check的妙用


    一般对于不容易正向求解的题,我们往往可以枚举答案并进行check,而对于具有单调性的答案或其映射来说,我们可以二分答案并进行check。

    例题1:

    codeforces 1288 D - Minimax Problem  (传送门)

    题意:

    给定n行 长为m的序列;

    找出任意两行序列,使得 两行序列 所有 两个相同索引的最大值 的最小值最大;

    分析:

    这道题朴素做法是O(n^2) ,但想在规定时间复杂度正向推出此题答案是非常不容易的;

    我们可以考虑枚举 最大的最小值,再用check函数加以检验,

    设当前枚举的答案为x,

    对于比x小的数,令其为0,否则令其为1,最后判断两个序列对应的01串的相或的结果是否为 全1,就能判断这个x是否可能成立(本来 答案是5 ,而x是3,但是相或也能得到 全1,虽然x可能不是题目要求的bi,但是由于要求解最值,所以check成功的最大的x一定是bi),而我们则是要找到这个最大的可能;

    显然,x是否可能成立是具有单调性的,所以可以用二分答案;

    注意,题目的m最大为8,而01排列最多就只有 2^8种,我们可以枚举01排列 进行check,而不是O(n^2)枚举所有序列。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N=1e6+5;
    const int inf=0x3f3f3f3f;
    const int mod=1e9+7;
    const double eps=1e-6;
    const long double pi=acos(-1.0L);
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define pb push_back
    #define mk make_pair
    #define mem(a,b) memset(a,b,sizeof(a))
    LL read()
    {
        LL x=0,t=1;
        char ch;
        while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    int lim,n,m,ansl,ansr;//lim是全1
    int a[N][10],vis[305];
    int judge(int x)
    {
        mem(vis,0);
        for(int i=1;i<=n;i++)
        {
            int t=0;
            for(int j=1;j<=m;j++)
                if(a[i][j]>=x) t|=1<<(j-1);//标记所有01排列
            vis[t]=i;
        }
        for(int i=0;i<=lim;i++)
            for(int j=i;j<=lim;j++)
                if((i|j)==lim&&vis[i]&&vis[j])
                {
                    ansl=vis[i]; ansr=vis[j];
                    return 1;
                }
        return 0;
    }
    int main()
    {
        n=read(),m=read();
        lim=(1<<m)-1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                a[i][j]=read();
        int l=0,r=1e9;
        ansl=1,ansr=1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(judge(mid)) l=mid+1;
            else r=mid-1;
        }
        printf("%d %d
    ",ansl,ansr);
        return 0;
    }
  • 相关阅读:
    ARM标准汇编与GNU汇编
    使用友元,编译出错fatal error C1001: INTERNAL COMPILER ERROR (compiler file 'msc1.cpp', line 1786) 的解决
    C++中值传递,引用传递,指针传递
    C++命名空间的用法
    关于初始化C++类成员
    vivi的配置与编译
    C++ 容器
    vivi分区问题,及移植时需要修改的地方(转)
    基于S3C2410的VIVI移植
    拷贝构造函数什么时候调用?
  • 原文地址:https://www.cnblogs.com/DeepJay/p/12225769.html
Copyright © 2020-2023  润新知