看到最小值最大一般会想到二分。
二分mid,把a中大于等于mid的看成1,小于mid的看成0。
对于每一行的0和1,我们能把这一行看成二进制中的一个数,设这个二进制数是c[i]。
如果能找到两行,这两行的c或运算起来全是1,那mid就是合法的。
也就是说,对于c[i],如果存在c[j],满足c[j]|(((1<<m)-1)^c[i])=c[j],那就说明mid是合法的。
处理一个vis数组,vis[i]表示是否存在某一个c[k]满足c[k]|vis[i]=c[k]即可。
vis的处理可以补位处理
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,l,r,ans;
const int N=100010,M=16;
int a[N][M],vis[(1<<15)+10];
int check(int mid)
{
for(int i=0,maxx=(1<<m)-1;i<=maxx;++i)vis[i]=0;
for(int i=1,now;i<=n;++i)
{
now=0;
for(int j=1;j<=m;++j)
if(a[i][j]>=mid)now|=1<<(j-1);
vis[now]=1;
}
for(int i=(1<<(m-1))-1;i>=0;--i)
for(int j=1;j<=m;++j)
if(!(i&(1<<(j-1))))vis[i]|=vis[i|(1<<(j-1))];
for(int i=1,now;i<=n;++i)
{
now=0;
for(int j=1;j<=m;++j)
if(a[i][j]>=mid)now|=1<<(j-1);
if(vis[((1<<m)-1)^now])return 1;
}
return 0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)scanf("%d",&a[i][j]);
l=-1;r=1e9+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}