Codeforces 1288D - Minimax Problem (状态压缩,枚举)

Description

思路

首先,求最大的最小值中的最大值本质是求最大值。
考虑用二分来枚举最大值是多少。假设枚举出了v,判断v是否可行。

将一行当中大于等于v的数设为1,小于v的数设为0。假设存在两行的01串可以互补成全1的串,说明存在最大值至少大于等于v,即v可行;反之不可行。

由于行数有\(10^5\)那么多,而01串的情况数不多于1000。枚举任意两行之间是否互补不如枚举满足互补的状态对应的行是否存在。因此用二进制状压。

用id[i]代表状态 i 的行号,若存在状态i,j满足(i | j)==(1<<m) - 1,那么id[i],id[j]就是满足条件的行号。

注意事项

二分的细节不要搞错,要确保不陷入死循环。
本题二分while里面需包含=,r = mid - 1,l = mid + 1,最终r为最大值。

相关:bitset的用法,二分搜索技巧

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 500000
#define maxm 10

int arr[maxn][maxm];
int id[1 << maxm];
int ans1, ans2;
int n, m;

bool check(int v) {
    memset(id, 0, sizeof id);
    for(int i = 0; i < n; i++) {
        int tmp = 0;
        for(int j = 0; j < m; j++) {
            if(arr[i][j] >= v) tmp |= (1 << j);
        }
        id[tmp] = i + 1;
    }

    for(int i = 0; i < (1 << m); i++) {
        for(int j = 0; j < (1 << m); j++) {
            if((i | j) == (1 << m) - 1) {
                if(id[i] && id[j]) {
                    ans1 = id[i];
                    ans2 = id[j];
                    
                    return true;
                }
            }
        }
    }
    return false;
}


int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    int mx = 0;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            cin >> arr[i][j];
            mx = max(mx, arr[i][j]);
        }
    }
    int l = 0, r = mx;
    while(l <= r) {
        int mid = (l + r) / 2;
        if(check(mid)) l = mid + 1;
        else r = mid - 1;
    }
    cout << ans1 << " " << ans2;
}

posted @ 2020-04-14 11:56  limil  阅读(132)  评论(0编辑  收藏  举报