【題解】Codeforces 1288D. Minimax Problem

【題目敘述】https://codeforces.com/problemset/problem/1288/D
【解題想法】二分搜,位元運算

  • 【例子】題目給出6個陣列:
    • a1 = [5 0 3 1 2]
    • a2 = [1 8 9 1 3]
    • a3 = [1 2 3 4 5]
    • a4 = [9 1 0 3 7]
    • a5 = [2 3 0 6 3]
    • a6 = [6 4 1 7 0]
    • 挑選其中兩個陣列(可以選擇同一個陣列兩次),形成一個新陣列。新陣列的每個元素為原來兩個陣列的「最大值」。
    • 比如,此例答案為 a1 和 a5,形成的新陣列為 [5 3 3 6 3]。
    • 題目要求,找出兩個陣列,讓 min ( [5 3 3 6 3] ) = 3 的值最大。
  • 【做法】二分搜,找出 x 值,使用 x 來檢查每個陣列的每個元素,利用位元表示每一個元素是否大於等於 x。
    • 例如:陣列 a1 = [5 0 3 1 2] 在 x = 5 時的位元表示式為 10000。如果存在另一個陣列 (ai) 的位元表示式與 10000 「OR 之後的結果」為 11111,則表示此 x 是可行的答案,而 a1 與 ai 是其中一組解。
    • 找出最大的 x 值,印出一組符合條件的陣列編號。
#include <iostream>
#include <vector>
using namespace std;
int n, m;
int a[300005][8];
int a1, a2;

bool check(int x){
    // 2^m 種位元組合,初始值-1(沒有任一陣列歸類其中)
    vector <int> mask(1 << m, -1);
    for (int i=0; i<n; i++){
        int now = 0;
        for (int j=0; j<m; j++){
            if (a[i][j] >= x){
                now |= (1 << j);
            }
        }
        mask[now] = i; //第i個陣列屬於這種位元組合
    }
    //有某個陣列所有元素均大於x
    //選擇兩個陣列為同一陣列
    if (mask[(1 << m) - 1] != -1){
        a1 = a2 = mask[(1 << m) - 1];
        return true;
    }
    //枚舉所有可能的配對
    for (int i=0; i< (1 << m); i++){
        for (int j=0; j< (1 << m); j++){
            if (mask[i] != -1 && mask[j] != -1 && (i | j) == (1 << m) - 1){
                a1 = mask[i];
                a2 = mask[j];
                return true;
            }
        }
    }
    return false;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    for (int i=0; i<n; i++){
        for (int j=0; j<m; j++){
            cin >> a[i][j];
        }
    }
    int l = 0;
    int r = 1e9 + 100;
    while (r - l > 1){
        int mid = (l + r) / 2;
        if (check(mid)) l = mid;
        else r = mid;
    }
    cout << a1 + 1 << ' ' << a2 + 1 << '\n';
    return 0;
}
分享本文 Share with friends