原題:
D. Minimax Problem
time limit per test
5 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output
給你n個序列,,每個序列包含m個整數。假設第x個序列的第y個元素可以表示爲。現在需要找到兩個序列和,這裏i可以等於j。並且,需要構造一個新的序列b,長度爲m,對於序列b中的每個數,要求,現在要求你選擇這樣兩個序列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<bits/stdc++.h>
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這個數是否可以在滿足題目規則要求的情況下達到。
假如選擇了兩行,和,兩個序列的長度是m,要選擇兩個序列中對應下標較大值組成新的序列,且滿足新序列中最小值是mid。 那麼,序列中比mid小的數字,對應到序列序列中的數字,一定要大於等於mid,例如。
mid = 3
序列比mid小的數字爲[1,0],那麼對應到序列中下標的數字一定要大於等於3才能滿足要求,如中對應下標的數字是[4,5],取最大值後得到的結果爲[5,4,5,3,4]。
那麼,可以使用位運算來進行判斷一個序列中如果數字大於等於mid,數字對應位數可以設置爲1,小於mid的可以設置爲0,那麼兩個序列可以用兩個整數表示,如果這兩個數通過取或運算後全部爲1,那麼則說明可以達到要求。這樣便滿足時間複雜度限制