题目链接:https://codeforces.com/contest/1288/problem/D
题目大意:
有n个数组,每个数组有m个数字,选择其中两个数组会形成一个新数组b,方法是,对于每一个位置,挑选两个数组中该位较大的数字。求使b数组中最小值最大的是哪两行
题目思路:
n高达3e5,暴力直接歇逼。看到最小值最大马上就想到了二分,但是一秒钟就把自己否了。。还是不够自信,看到只有600+过题人数就虚了。。后来还是看了大佬的,感觉思路确实很简单但是非常巧妙。由于m非常小,最多8。这说明了什么?这说明状态非常少!直接二分答案,然后每一行用二进制表示,如果这一位>=x(x就是被二分的值),就说明这一行无论跟谁在一起,这一位都有这行打包票救住。由于状态非常非常少,所以直接n^2枚举状态,要求只有两个,一个是这两个状态或起来是全集,也就是每一列都有人能保,还有一个就是这个状态有行满足,这个就需要id数组,记录每个状态对应的行,然后输出即可。
以后遇到数据范围特别小的就要想到状态枚举,也不要因为看到状态枚举就觉得很难,可能非常简单!
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 3e5+5;
const int MOD = 1e9+7;
int a[MAXN][10],n,m;
int x,y,id[1000];
bool check(int mid){
memset(id,0,sizeof(id));
rep(i,1,n){
int temp=0;
rep(j,1,m){
if(a[i][j]>=mid){
temp|=(1<<(j-1));
}
}
id[temp]=i;
}
int endd=(1<<m)-1;
rep(i,0,endd){
rep(j,0,endd){
if((i|j)==endd&&id[i]&&id[j]){
x=id[i],y=id[j];
return 1;
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
rep(i,1,n){
rep(j,1,m)scanf("%d",&a[i][j]);
}
int l=0,r=1e9+7;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
l=mid+1;
}
else r=mid-1;
}
cout<<x<<" "<<y<<endl;
return 0;
}