Minimax Problem
Problem Description
You are given n arrays a1, a2, …, an; each array consists of exactly m integers. We denote the y-th element of the x-th array as ax,y.
You have to choose two arrays ai and aj (1≤i,j≤n, it is possible that i=j). After that, you will obtain a new array b consisting of m integers, such that for every k∈[1,m], bk=max(ai,k,aj,k).
Your goal is to choose i and j so that the value of min k = 1 m b k \min\limits_{k=1}^mb_k k=1minmbk is maximum possible.
Input
The first line contains two integers n and m (1≤n≤3⋅105, 1≤m≤8) — the number of arrays and the number of elements in each array, respectively.
Then n lines follow, the x-th line contains the array ax represented by m integers ax,1, ax,2, …, ax,m (0≤ax,y≤109).
Output
Print two integers i and j (1≤i,j≤n, it is possible that i=j) — the indices of the two arrays you have to choose so that the value of min k = 1 m b k \min\limits_{k=1}^mb_k k=1minmbk is maximum possible. If there are multiple answers, print any of them.
Sample 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
Sample Output
1 5
题意
有 n 个数组,每个数组有 m 个元素,可以任选两个数组(可以相同)组成一个新的数组 b,数组b的每个位置的数为两个数组中对应位置较大的数。求应该选择哪两个数组,可以使数组b的最小值最大。
题解:
思路一:最大化最小值,可以考虑二分。普通的二分套路,就是检验的时候需要状压。当前检查mid是否满足时,n个数组,首先求出每个数组的状态,m个二进制位,如果数组的第 i 位大于等于 mid ,则第 i 位为 1,否则为0。这样可以求出每个数组的状态,最多可能有
1
<
<
m
1<<m
1<<m种状态,对于每种状态,如果有数组的状态与其一样,则记录是哪个数组(多个随便记录一个即可),否则保持初始化的值。
因为m很小,状态总数也就很小,那平方级暴力检验是否有两种状态的或运算为
(
1
<
<
m
)
−
1
(1<<m)-1
(1<<m)−1,即或的结果所有二进制位都为1。如果有则说明mid检验为真。
思路二:暴力+贪心+状压。(下面描述的乱七八糟的,如果看不懂的话,还是写二分吧,不过两种思路的核心都是二分)考虑对所有的n*m个数排序,然后从大到小依次考虑每个值插入原数组后能否找到满足要求的数组。如果有两个数组,他们的并使m个位置都被填充插入过,则是一个最优解。
在插入时,维护每个数组的状态(每个位置的数是否被插入),如果当前数组的第 j 个数插入,则更新当前数组状态的第j位为1,这里可以利用位运算,因为m不大,所以可以参考状压dp的状态记录的方法。
在更新完状态后,设当前数组为u,状态数组为a,若存在数组 v 是
a
[
u
]
∣
a
[
v
]
a[u] | a[v]
a[u]∣a[v]的m位二进制位都为1,则u,v是答案,因为如果暴力试每个数组,单次就O(n)的复杂度。所以可以考虑检查状态数组b(即下面代码中的b数组)。
例如:两个长度为3的数组
6 5 4
3 2 9
step | 动作 | 数组1状态(二进制) | 数组2状态(二进制) |
---|---|---|---|
0 | 000 | 000 | |
1 | 插入第二个数组第3个数 | 000 | 001 |
2 | 插入第一个数组第1个数 | 100 | 001 |
3 | 插入第一个数组第2个数 | 110 | 001 |
存在数组与第一个数组当前状态满足所有位置都有数字,算法结束。
思路一代码(二分):
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-6
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 300100;
const int mod = 1000000007;
int ans1, ans2, a[maxn][10], b[520];
bool isok(int mid, int n, int m);
int main()
{
int n, m, i, j, k, l = 0, r = 0;
scanf("%d %d", &n, &m);
for(i=1;i<=n;i++)
for(j=0;j<m;j++)
{
scanf("%d", &a[i][j]);
r = max(r, a[i][j]);
}
while(l<r)
{
int mid = (l+r+1)/2;
if(isok(mid, n, m))
l = mid;
else r = mid-1;
}
isok(l, n, m);
printf("%d %d\n", ans1, ans2);
return 0;
}
//二分检验结果
bool isok(int mid, int n, int m)
{
int up = (1<<m)-1, i, j;
for(i=0;i<=up;i++)b[i] = -1;
for(i=1;i<=n;i++)
{
int tu = 0;
for(j=0;j<m;j++)
if(a[i][j] >= mid)
tu |= 1<<j;
b[tu] = i;
}
for(i=0;i<=up;i++)
for(j=0;j<=up;j++)
{
//i|j需要带括号,优先级的问题调了半天
if((i|j) == up && b[i] != -1 && b[j] != -1)
{
ans1 = b[i], ans2 = b[j];
return true;
}
}
return false;
}
思路二代码(我觉得我描述的可能只有我自己听的懂吧):
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<deque>
#include<map>
#include<iostream>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 300100;
const int mod = 1000000007;
struct node{
int i, j, p;
}p[maxn*8];
int a[maxn], b[1020];
bool cmp(node a, node b);
int main()
{
int n, m, i, j, k, num = 0, sig = 0, up;
int ans1, ans2;
scanf("%d %d", &n, &m);
up = (1<<m)-1;
for(i=1;i<=n;i++)
for(j=0;j<m;j++){
scanf("%d", &p[num].p);
p[num].i = i, p[num].j = j;
num++;
}
num = n*m;
memset(a, 0, sizeof(a));
memset(b, -1, sizeof(b));
sort(p, p+num, cmp);
for(i=0;i<num;i++)
{
int u = p[i].i;
a[u] = a[u] | (1<<p[i].j);
b[a[u]] = u;
if(a[u] == up){
ans1 = ans2 = u;
sig = 1;
break;
}
for(j=0;j<=up;j++)
if(((a[u] | j) == up) && b[j] != -1)
{
sig = 1;
ans1 = u;
ans2 = b[j];
break;
}
if(sig == 1)break;
}
printf("%d %d\n", ans1, ans2);
return 0;
}
bool cmp(node a, node b)
{
return a.p > b.p;
}