2021字节跳动研发春招第五场笔试第三题

题目大意

给出 n ( 2 ≤ n ≤ 1 e 5 ) n(2 \leq n \leq 1e5) n(2n1e5)个五元组 p i = { a i , b i , c i , d i , e i } p_i = \{a_i,b_i,c_i,d_i,e_i\} pi={ ai,bi,ci,di,ei},要从中选出2个五元组 i , j i, j i,j,最终的得分为 m i n { m a x ( a i , a j ) , m a x ( b i , b j ) , m a x ( c i , c j ) , m a x ( d i , d j ) , m a x ( e i , e j ) } min\{max(a_i, a_j), max(b_i, b_j),max(c_i, c_j),max(d_i, d_j), max(e_i, e_j)\} min{ max(ai,aj),max(bi,bj),max(ci,cj),max(di,dj),max(ei,ej)},问最多的得分是多少。

解题思路

典型的"最大的最小"型问题,考虑二分答案 x x x,本题的难点是如何写 c h e c k ( ) check() check()函数。

对于当前要判断的答案 x x x,先扫一遍,考虑用状压 s t a sta sta存储每个元素相对于 x x x的大小,每个五元组的每个元素分别和 x x x比较大小,若大于等于 x x x那么置为 1 1 1,否则置为 0 0 0。因此问题变成了如何找到两个不同的 s t a sta sta,使得 s t a i ∣ s t a j = 11111 sta_i | sta_j = 11111 staistaj=11111。状态的个数很少,又因为要找最小的下标,因此用 s e t set set存储每个状态的下标(实际上可以只存两个最小的),然后枚举状态 i , j i, j i,j,查看是否存在 s t a i ∣ s t a j = 11111 sta_i | sta_j = 11111 staistaj=11111

一开始想到的是对于二分的答案,要在一个五元组中先找到一个恰好存在这个答案的五元组,然后再枚举。但实际上没必要这样,只要存在大于等于该答案的情况,那么最终的答案一定会更大,这是二分答案需要注意的细节。

因为没地方提交,我自己造了几组数据测试都没问题,时间复杂度为 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
const int maxn = 1e5 + 10;

struct node {
    
    
    int a[5];
    int id, sta;
} p[maxn];

set<int> s[35];
int n, ans, ans1, ans2;
bool flag;

void upd(int x, int y) {
    
    
    if (!flag) {
    
    
        flag = 1;
        ans1 = x, ans2 = y;
    } else {
    
    
        if (ans1 > x)
            ans1 = x, ans2 = y;
        else if (ans1 == x)
            ans2 = min(ans2, y);
    }
}

bool check(int x) {
    
    
    flag = 0;
    for (int i = 0; i < 32; i++) s[i].clear();
    for (int i = 1; i <= n; i++) {
    
    
        p[i].sta = 0;
        for (int j = 0, k = 1; j < 5; j++, k *= 2) {
    
    
            if (p[i].a[j] >= x) p[i].sta += k;
        }
        s[p[i].sta].insert(p[i].id);
    }
    bool ok = 0;
    for (int i = 0; i < 32; i++) {
    
    
        if (s[i].empty()) continue;
        for (int j = 0; j < 32; j++) {
    
    
            if (s[j].empty() || (i == j && s[i].size() < 2)) continue;
            if ((i | j) == 31) {
    
    
                int a1, b1;
                if (i == j)
                    a1 = *s[i].begin(), b1 = *next(s[i].begin());
                else
                    a1 = *s[i].begin(), b1 = *s[j].begin();
                ok = 1;
                if (a1 > b1)
                    upd(b1, a1);
                else
                    upd(a1, b1);
            }
        }
    }
    return ok;
}

bool duipai() {
    
      //对拍程序
    int res = 0;
    for (int i = 1; i <= n; i++) {
    
    
        for (int j = i + 1; j <= n; j++) {
    
    
            int cur = inf;
            for (int k = 0; k < 5; k++) {
    
    
                cur = min(cur, max(p[i].a[k], p[j].a[k]));
            }
            res = max(res, cur);
        }
    }
    for (int i = 1; i <= n; i++) {
    
    
        for (int j = i + 1; j <= n; j++) {
    
    
            int cur = inf;
            for (int k = 0; k < 5; k++) {
    
    
                cur = min(cur, max(p[i].a[k], p[j].a[k]));
            }
            if (res == cur) {
    
    
                if (ans == res && ans1 == i && ans2 == j) return 1;
                // cout << ans << " " << ans1 << " " << ans2 << ENDL;
                // cout << res << " " << i << " " << j << ENDL;
                return 0;
            }
        }
    }
}

/*
3
8
1 3 6 1 7
5 3 5 6 1
5 0 0 7 7
0 4 1 2 4
4 7 6 7 2
3 4 4 4 7
3 1 5 3 1
6 6 3 5 7
4
5 4 3 10 1
8 6 7 3 10
9 5 3 10 10
2 6 6 8 5
6
4 8 9 1 7
1 9 4 3 4
4 7 9 6 7
2 9 4 4 6
9 9 10 10 4
7 2 5 9 5
*/

int main() {
    
    
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
    
    
        cin >> n;
        for (int i = 1; i <= n; i++)
            for (int j = 0; j < 5; j++) {
    
    
                cin >> p[i].a[j];
                p[i].id = i;
            }
        int l = 0, r = maxn;
        while (l <= r) {
    
    
            int mid = (l + r) >> 1;
            if (check(mid)) {
    
    
                ans = mid;
                l = mid + 1;
            } else
                r = mid - 1;
        }
        // if (!duipai())
        //     cout << "WA" << ENDL;
        // else
        //     cout << "AC" << ENDL;
        cout << ans << " " << ans1 << " " << ans2 << ENDL;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/115677939