一、题目链接
https://codeforces.com/contest/1288/problem/D
二、题意
给定n个数组 a 1 a_1 a1, a 2 a_2 a2,……, a n a_n an, 每个数组有m个整数。用 a x , y a_{x,y} ax,y 描述第x个数列的第y个数。
你可以选出任意数列 a i a_i ai 、 a j a_j aj(1≤i,j≤n,i与j可以相等),从中得到同样有m个整数的新数列{ b m b_m bm},使得对于任意k∈[1,m] b k b_k bk = max( a i , k a_{i,k} ai,k, a j , k a_{j,k} aj,k)
对于一个数列{ b m b_m bm},存在最小值min。有你的目标是找出所有min的最大值。输出这个值所在的两行行序号。(从1开始)
三、数据范围
1 ≤ n ≤ 3 * 1 0 5 10^5 105
1 ≤ m ≤ 8
四、解题思路
一句话概括题意,找同列两个数最大值的同行最小值的最大值。
寻找“最小值的最大值”,可以想到二分查找。怎么判断当前mid是否为正确结果呢?
在某个新获取的数列{ b m b_m bm}中,存在最小值min。由题意,只需要确认 mid = min 在一个数列{ b m b_m bm}成立,就可以更新下界,进行下一步查找了。但仅仅枚举数列{ b m b_m bm}就是n*n*m,我们需要更优的算法。
数列{ b m b_m bm}的获取方式是比较某两个数列对应位置的数字,再加上m数据范围的特殊性,我们有了按位或的思路。
对于数列 a i a_i ai 、 a j a_j aj,将其中≥min的数字换成1,<min的数字换成0,再进行按位或,必将得到一个全为1构成的数列P。
我们来看一下按位或的规则:
1|0=1
1|1=1
0|0=0
0|1=1
其实这个操作过程就是题目的“取大值”。数列P即为按上述规则修改后的数列b。
以样例第一行和第五行为例:
由
5 0 3 1 2
2 3 0 6 3
得到
5 3 3 6 3 → min=3
修改原数列:
5 0 3 1 2 → 1 0 1 0 0
2 3 0 6 3 → 0 1 0 1 1
按位或后 → 1 1 1 1 1
核心思路:二分查找时,对每一行,将>=mid的值改为1,<mid的数字改为0,一行转存为一个二进制数。得到的二进制数范围为[0, 2 m 2^m 2m) 。在这个范围进行按位或,若得到 2 m − 1 2^m-1 2m−1,(含m个1的二进制数),则mid = min。这个mid保存为可能的答案。
五、AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, a[300005][10], ans, ans1, ans2, x[300];
//ans是题目寻找的最大值,ans1、ans2为要求输出的行序号
bool check(int k)
{
memset(x, 0, sizeof(x)); //记得初始化!!!
for (int i = 0; i < n; i++)
{
int tot = 0; //tot为这一行转换01后的二进制数
for (int j = 0; j < m; j++)
if (a[i][j] >= k) tot += (1 << j);
//else tot += (0 << j);
x[tot] = i + 1; //x[num]记录该数列在第几行
}
for (int i = 0; i < (1 << m); i++)
for (int j = i; j < (1 << m); j++)
{
if (x[i] && x[j] && (i | j) == (1 << m) - 1)
//x[i]和x[j]表示的二进制数都存在于输入中
{
ans1 = x[i];
ans2 = x[j];
return 1;
}
}
return 0;
}
int main() {
cin >> n >> m;
int l = 1000000000, r = -1;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
cin >> a[i][j];
if (a[i][j] < l) l = a[i][j];
if (a[i][j] > r) r = a[i][j];
}
ans = l;
while(l <= r) //二分查找模板
{
int mid = (l + r) / 2;
if (check(mid))
{
l = mid + 1;
ans = mid;
}
else r = mid - 1;
}
cout << ans1 << ' ' << ans2;
return 0;
}
呜呜呜,俺也是刚开始写题解,若有表述不清还请多多包涵QAQ