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

algorithm-找到多少个不能赢得比赛的玩家?

(algorithm - Find the number of players cannot win the game?)

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

我们给了n个玩家,每个玩家有3个值分别分配给A,B和C。

如果存在另一个具有所有三个值A [j]> A [i],B [j]> B [i]和C [j]> C [i]的玩家j玩家i无法获胜我们被要求找到无法获胜的玩家数量。

我使用蛮力尝试了这个问题,蛮力是对玩家数组的线性搜索。但是它显示TLE

对于每个玩家i,我遍历整个数组以查找是否存在满足上述条件的其他玩家j

代码 :

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;
}

这个方法是O(n ^ 2),因为对于每个玩家,我们都遍历整个数组。因此,它给了TLE。

样本测试用例

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.

限制条件:

n(number of players) <= 10^5

解决该问题的最佳方法是什么?

解决方案:

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;
}

该解决方案使用段树,从而解决了时间O(n * log(n))和空间O(n)的问题

方法在@Primusa接受的答案中进行了解释。

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

首先让我们假设我们的输入是以元组列表的形式出现的 T = [(A[0], B[0], C[0]), (A[1], B[1], C[1]) ... (A[N - 1], B[N - 1], C[N - 1])]

我们可以做的第一个观察是,我们可以排序T[0](以相反的顺序)。然后对于每个元组(a, b, c),以确定它是否赢不了,我们要问,如果我们已经看到了一个元组(d, e, f)这样e > b && f > c我们不需要检查第一个元素,因为给定d > a*,因为它T是反向排序的。

好的,那么现在我们如何检查第二个标准?

我们可以这样重组:(d, e, f)在已经看到的所有元组中e > b的最大值是f多少?如果最大值大于c,则我们知道该元组无法获胜。

为了处理这一部分,我们可以使用具有最大更新次数和最大范围查询量的细分树。当我们遇到一个元组时(d, e, f),我们可以进行设置tree[e] = max(tree[e], f)tree[i]将代表第三个元素而i成为第二个元素。

要回答这样一个查询“什么是最大值f,这样的e > b”,我们这样做max(tree[b+1...]),在一定范围的可能的第二要素得到最大的第三个元素。

由于我们仅在进行后缀查询,因此你可以使用经过修改的fenwick树,但是使用段树进行解释则更容易。

这将为我们提供一个O(NlogN)解决方案,用于对每个元组的段树进行排序TO(logN)处理。

*注意:这实际上应该是d >= a但是,当我们假装一切都是唯一的时,更容易解释该算法。你需要进行修改以容纳第一个元素的重复值的唯一修改是在相同值的元组存储桶中处理查询和更新。这意味着我们将对具有相同第一个元素的所有元组执行检查,然后tree[e] = max(tree[e], f) 才对执行检查的所有那些元组进行更新这样可以确保在另一个元组查询树时,没有具有相同第一个值的元组已经更新过该树。