D. Minimax Problem
题意:给n组长度为m的数组,n<=3*1e5,m<=8,可以合并两个数组,合并后的数组为b[i]=max(a[x][i],a[y][i]).求合并方法使合并两个数组后的那个数组中的值的最小值最大。
解析:首先最小值最大很容易想到二分,然后可以二分最小值为mid,然后问题转化成是否可以选择两个数组使最小值大于等于mid,因为m<=8,所以就可以状态压缩,大于等于mid的话就是1,小于mid就为0,然后判断两个数组合并后的最小值是否大于等于mid就是两个数组状态压缩之后的两个数或操作之后是否全为1,当然如果n个数组暴力的话复杂度是n的平方,肯定爆了,于是可以把n个数组状态压缩后的值映射成vis[1<<m]数组,这样就可以暴力找了因为vis数组的大小为1<<m,即最大2的8次方,所以2的八次方的平方为2的16次方不会爆,这个地方应该注意,因为思维惯性,想n的平方的复杂度肯定没法处理所以n的平方的方法一定不行,可是却忽略了映射之后的2的8次方的值很小,只有256,其平方也很小,是可以接受的时间复杂度,最后注意一下check函数初始化即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX_N=301000;
int a[MAX_N][10];
int ans1,ans2,n,m;
int b[MAX_N],vis[1010];
int fin;
bool check(int x){
//cout<<x<<" x \n";
int i,j;
for(i=0;i<(1<<m);i++)
vis[i]=0;
for(i=1;i<=n;i++)
b[i]=0;
for(i=1;i<=n;i++){
for(j=0;j<m;j++){
if(a[i][j]>=x)
b[i]+=(1<<j);
}
//cout<<b[i]<<" b[i]\n";
vis[b[i]]=i;
}
for(i=0;i<(1<<m);i++){
if(!vis[i])
continue;
for(j=0;j<(1<<m);j++){
if(vis[j]&&(i|j)==fin){
ans1=vis[i];
ans2=vis[j];
return true;
}
}
}
return false;
}
int main(void){
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
for(j=0;j<m;j++){
scanf("%d",&a[i][j]);
}
}
fin=(1<<m)-1;
int l=0,r=1e9,mid=(l+r)>>1;
int ans=-1;
while(l<=r){
if(check(mid)){
ans=mid;
l=mid+1;
}
else{
r=mid-1;
}
mid=(l+r)>>1;
}
//cout<<mid<<"\n";
check(mid);
printf("%d %d\n",ans1,ans2);
return 0;
}