Warm tip: This article is reproduced from serverfault.com, please click

Find the number of players cannot win the game?

发布于 2020-11-29 05:43:05

We are given n players, each player has 3 values assigned A, B and C.

A player i cannot win if there exists another player j with all 3 values A[j] > A[i], B[j] > B[i] and C[j] > C[i]. We are asked to find number of players cannot win.

I tried this problem using brute force, which is a linear search over players array. But it's showing TLE.

For each player i, I am traversing the complete array to find if there exists any other player j for which the above condition holds true.

Code :

int count_players_cannot_win(vector<vector<int>> values) {
    int c = 0;
    int n = values.size();
    for(int i = 0; i < n; i++) {
        for(int j = 0; j!= i && j < n; j++) {
            if(values[i][0] < values[j][0] && values[i][1] < values[j][1] && values[i][2] < values[j][2]) { 
                c += 1;
                break;
           }
        }    
    }
    return c;
}

And this approach is O(n^2), as for every player we are traversing the complete array. Thus it is giving the TLE.

Sample testcase :

Sample Input

3(number of players)
A B C
1 4 2
4 3 2
2 5 3

Sample Output :

1 

Explanation :
Only player1 cannot win as there exists player3 whose all 3 values(A, B and C) are greater than that of player1.

Contraints :

n(number of players) <= 10^5

What would be optimal way to solve this problem?

Solution:

int n;
const int N = 4e5 + 1;
int tree[N];

int get_max(int i, int l, int r, int L) {  // range query of max in range v[B+1: n]
    if(r < L || n <= l)
        return numeric_limits<int>::min();
    else if(L <= l)
        return tree[i];
    int m = (l + r)/2;
    return max(get_max(2*i+1, l, m, L), get_max(2*i+2, m+1, r, L));
}
void update(int i, int l, int r, int on, int v) { // point update in tree[on]
    if(r < on || on < l) 
        return;
    else if(l == r) {
        tree[i] = max(tree[i], v);
        return;
    }
    int m = (l + r)/2;
    update(2*i+1, l, m, on, v);
    update(2*i+2, m + 1, r, on, v);
    tree[i] = max(tree[2*i+1], tree[2*i+2]);
}

bool comp(vector<int> a, vector<int> b) {
    return a[0] != b[0] ? a[0] > b[0] : a[1] < b[1];  
}

int solve(vector<vector<int>> &v) {
    n = v.size();
    vector<int> b(n, 0);           // reduce the scale of range from [0,10^9] to [0,10^5]
    for(int i = 0; i < n; i++) {
        b[i] = v[i][1];
    }
    for(int i = 0; i < n; i++) {
        cin >> v[i][2];
    }

//     sort on 0th col in reverse order
    sort(v.begin(), v.end(), comp);
    sort(b.begin(), b.end());
    
    int ans = 0;
    for(int i = 0; i < n;) {
        int j = i;
        while(j < n &&  v[j][0] == v[i][0]) {    
            int B = v[j][1];
            int pos = lower_bound(b.begin(), b.end(), B) - b.begin(); // position of B in b[]
            int mx = get_max(0, 0, n - 1, pos + 1);
            if(mx > v[j][2])
                ans += 1;
            j++;
        }
        while(i < j) {
            int B = v[i][1];
            int C = v[i][2];
            int pos = lower_bound(b.begin(), b.end(), B) - b.begin(); // position of B in b[]
            update(0, 0, n - 1, pos, C);
            i++;
        }
    }
    return ans;
}

This solution uses segment tree, and thus solves the problem in time O(n*log(n)) and space O(n).

Approach is explained in the accepted answer by @Primusa.

Questioner
tusharRawat
Viewed
0
Primusa 2020-11-29 16:26:23

First lets assume that our input comes in the form of a list of tuples T = [(A[0], B[0], C[0]), (A[1], B[1], C[1]) ... (A[N - 1], B[N - 1], C[N - 1])]

The first observation we can make is that we can sort on T[0] (in reverse order). Then for each tuple (a, b, c), to determine if it cannot win, we ask if we've already seen a tuple (d, e, f) such that e > b && f > c. We don't need to check the first element because we are given that d > a* since T is sorted in reverse.

Okay, so now how do we check this second criteria?

We can reframe it like so: out of all tuples (d, e, f), that we've already seen with e > b, what is the maximum value of f? If the max value is greater than c, then we know that this tuple cannot win.

To handle this part we can use a segment tree with max updates and max range queries. When we encounter a tuple (d, e, f), we can set tree[e] = max(tree[e], f). tree[i] will represent the third element with i being the second element.

To answer a query like "what is the maximum value of f such that e > b", we do max(tree[b+1...]), to get the largest third element over a range of possible second elements.

Since we are only doing suffix queries, you can get away with using a modified fenwick tree, but it is easier to explain with a segment tree.

This will give us an O(NlogN) solution, for sorting T and doing O(logN) work with our segment tree for every tuple.

*Note: this should actually be d >= a. However it is easier to explain the algorithm when we pretend everything is unique. The only modification you need to make to accommodate duplicate values of the first element is to process your queries and updates in buckets of tuples of the same value. This means that we will perform our check for all tuples with the same first element, and only then do we update tree[e] = max(tree[e], f) for all of those tuples we performed the check on. This ensures that no tuple with the same first value has updated the tree already when another tuple is querying the tree.