原题:
D. Minimax Problem
time limit per test
5 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output
给你n个序列, a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,每个序列包含m个整数。假设第x个序列的第y个元素可以表示为 a x , y a_{x,y} ax,y。现在需要找到两个序列 a i a_i ai和 a j a_j aj,这里i可以等于j。并且,需要构造一个新的序列b,长度为m,对于序列b中的每个数 b k b_k bk,要求 b k = m a x ( a i , k , a j , k ) b_k=max(a_{i,k},a_{j,k}) bk=max(ai,k,aj,k),现在要求你选择这样两个序列i和j,使得序列b中最小的的数最大。其中m∈[1,8],n属于[1,3*10^5]
Example
Input
6 5
5 0 3 1 2
1 8 9 1 3
1 2 3 4 5
9 1 0 3 7
2 3 0 6 3
6 4 1 7 0
Output
1 5
代码:
#include
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 3e5 + 1;
const int maxm = 8 + 1;
const ll mod = 1e9 + 7;
int n,m;
int a[maxn][maxm];
int line[1<<maxm];
int x,y;
bool solve(int mid)
{
memset(line,-1,sizeof(line));
for(int i=1;i<=n;i++)
{
int tmp = 0;
for(int j=1;j<=m;j++)
{
if(a[i][j]>=mid)
tmp |=(1<<(j-1));
}
if(tmp == (1<<m)-1)
{
x=y=i;
return true;
}
line[tmp] = i;
}
for(int i=0;i<(1<<m);i++)
{
for(int j=0;j<(1<<m); j++)
{
if(line[i] != -1 && line[j] != -1 && (i|j) == (1<<m)-1)
{
x = line[i];
y = line[j];
return true;
}
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
int inf = 0;
while(cin>>n>>m)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
inf = max(inf,a[i][j]);
}
}
x = y = n;
int L = 0, R = inf + 2;
while(R - L > 1)
{
int mid = (L + R) >> 1;
if(solve(mid))
L = mid;
else
R = mid;
}
cout<<x<<" "<<y<<endl;
}
return 0;
}
解答:
找最小值最大的这种问题基本上都是二分,遇到数据范围小于20的,基本上都可以往位运算上面想一想 -_-||
此题目也不例外,如果想到用二分找极大值极小,那么问题解决了一半,首先二分枚举可以找到的最小值mid,然后通过一个函数solve(mid)判断当前最小值是否可以达到,如果可以达到,那么把二分查找的区间扩大。
现在关键在于如何快速的判断mid这个数是否可以在满足题目规则要求的情况下达到。
假如选择了两行, a i a_i ai和 a j a_j aj,两个序列的长度是m,要选择两个序列中对应下标较大值组成新的序列,且满足新序列中最小值是mid。 那么,序列 a i a_i ai中比mid小的数字,对应到序列 a j a_j aj序列中的数字,一定要大于等于mid,例如。
a i = [ 5 , 1 , 0 , 3 , 4 ] a_i=[5,1,0,3,4] ai=[5,1,0,3,4] a j = [ 3 , 4 , 5 , 2 , 1 ] a_j=[3,4,5,2,1] aj=[3,4,5,2,1] mid = 3
序列 a i 中 a_i中 ai中比mid小的数字为[1,0],那么对应到 a j a_j aj序列中下标的数字一定要大于等于3才能满足要求,如 a j a_j aj中对应下标的数字是[4,5],取最大值后得到的结果为[5,4,5,3,4]。
那么,可以使用位运算来进行判断一个序列中如果数字大于等于mid,数字对应位数可以设置为1,小于mid的可以设置为0,那么两个序列可以用两个整数表示,如果这两个数通过取或运算后全部为1,那么则说明可以达到要求。这样便满足时间复杂度限制