• CodeForces 1288D. Minimax Problem (二分+位运算)


    传送门

    题意

    (n) 个数组,每个数组 (m(1le mle 8)) 个数,你需要选两个数组 (a,b),可以相同,使得 (Min_{1le ile m}{max(a_i,b_i)}) 最大。

    思路

    二分好题啊
    一般二分题可以从题目要求上明显看出“求符合要求的最值”这个意思,而且要能证明这个问题是单调的,就是说如果要求符合要求的最小值,那么比这个最小值小的值一定不符合要求,而比这个最小值大的值一定符合要求。还有一个条件就是,反向验证这个值比正向思考这个问题要简单。
    回到这道题,想想怎么验证答案。
    比如说要验证 (x) 是否合法,就是要验证从这 (n) 个数组中选 (2) 个出来,是否两个数组对应位置上较大的数都大于等于 (x)
    可以对于每个数组,考虑每一位数,把大于等于 (x) 的那一位置为 (1),小于置为 (0),那么我们发现可以用一个二进制数来表示这个数组。而且这个二进制数很小,最大 (2^8-1)
    那么把这个数对应的数组的编号记下来,然后看是否有两个数都表示了某个数组,并且这两个数取或为全 (1),即 (2^m-1),那么 (x) 合法,并且这两个数表示的数组编号也是对应满足要求的两个数组。
    不知道我写的是不是足够清晰,其实看看结合代码看一下应该能明白吧

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x3f3f3f3f3f3f3f3f;
    const int MAXN=3e5+10;
    int n,m,a[MAXN][10],mark[300],pos1,pos2,ans1,ans2;
    
    bool check(int x){
    	pos1=0,pos2=0;
    	memset(mark,0,sizeof(mark));
    	for(int i=1;i<=n;i++){
    		int now=0;
    		for(int j=0;j<m;j++) now+=((a[i][j]>=x)<<j);
    		mark[now]=i;
    	}
    	for(int i=0;i<(1<<m);i++)
    		for(int j=i;j<(1<<m);j++)
    			if((i|j)==(1<<m)-1&&mark[i]&&mark[j])
    				pos1=mark[i],pos2=mark[j];	
    	return pos1&&pos2;
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<m;j++)
    			scanf("%d",&a[i][j]);
    	int l=0,r=1e9;
    	while(l<r){
    		int mid=(l+r+1)>>1;
    		if(check(mid)) ans1=pos1,ans2=pos2,l=mid;
    		else r=mid-1;
    	}
    	check(l);ans1=pos1,ans2=pos2;
    	printf("%d %d
    ",ans1,ans2);
    	return 0;
    }
    
  • 相关阅读:
    宏的全解
    Mathematica 表达式求值
    nandflash中oob、ecc分析
    ubuntu 12.04下安装openldap,slapd.conf找不到的解决方法
    jetbrains
    看看美国公务员挣多少钱
    css的#和.的区别
    Ubuntu 14.04 LTS下安装Google Chrome浏览器
    Bootstrap 模态框(Modal)插件
    怎么旋转PDF文件的方向并保存成功
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/12220912.html
Copyright © 2020-2023  润新知