zoukankan      html  css  js  c++  java
  • [LeetCode] 1395. Count Number of Teams

    There are n soldiers standing in a line. Each soldier is assigned a unique rating value.

    You have to form a team of 3 soldiers amongst them under the following rules:

    • Choose 3 soldiers with index (ijk) with rating (rating[i]rating[j]rating[k]).
    • A team is valid if:  (rating[i] < rating[j] < rating[k]) or (rating[i] > rating[j] > rating[k]) where (0 <= i < j < k < n).

    Return the number of teams you can form given the conditions. (soldiers can be part of multiple teams).

     

    Example 1:

    Input: rating = [2,5,3,4,1]
    Output: 3
    Explanation: We can form three teams given the conditions. (2,3,4), (5,4,1), (5,3,1). 
    

    Example 2:

    Input: rating = [2,1,3]
    Output: 0
    Explanation: We can't form any team given the conditions.
    

    Example 3:

    Input: rating = [1,2,3,4]
    Output: 4
    

     

    Constraints:

    • n == rating.length
    • 1 <= n <= 200
    • 1 <= rating[i] <= 10^5

    The constraints of this problem permits a brute force O(N^3) solution by simply checking all possible triplets. However, if we increase the input size to 10^5,  we are required to come up with a more efficient algorithm to solve this problem.

    First point we should observe is that if we can come up with an efficient algorithm for condition (rating[i] < rating[j] < rating[k]) where (0 <= i < j < k < n), then we can simply reverse the input array and apply the same algorithm for condition (rating[i] > rating[j] > rating[k]) where (0 <= i < j < k < n).

    Let's walk through the following example to get an idea of how an efficient algorithm should work: 

    2 5 3 4 1 6

     

    If we only consider the increasing case, the answer is 5 shown as the follows.

    (2, 3, 4)

    (2, 5, 6)

    (2, 3, 6)

    (2, 4, 6)

    (3, 4, 6)

    Let's find all triplets by traversing the input array and make each element V as the largest number of a valid triplet if possible. If we denote each element V's smaller preceeding number count as Cnt(V) and the number of valid triplets ending at V as Sum(V). Then we have the following formula:

    Sum(V) = Cnt(1) + Cnt(2) + ..... + Cnt(V - 1).

    In another word, in order to find the number of valid triplets ending in V, we need to sum the contributions from 1 to V - 1 as the middle number. The contribution of each number in [1, V - 1] equals to its count of smaller proceeding numbers. 

    A walk through of provided example with this idea is:

    i = 0,  a[i] = 2;      Sum(2) = 0; Cnt(2) = 0;

    i = 1,  a[i] = 5;     2 < 5 but there isn't enough numbers to make an increasing triplet at this point. Sum(5) = 0; Cnt(5) = 1;

    i = 2, a[i] = 3, T = 0;      2 < 3 but 2 has no smaller number ahead of it to use as the smallest number in a triplet. Sum(3) = 0; Cnt(3) = 1;

    i = 3, a[i] = 4, T = 1;      3 has 1 smaller number 2 ahead of it. Sum(4) = Sum(1) + Sum(2) + Sum(3) = 1; Cnt(4) = 2;

    i = 4, a[i] = 1, T = 0;      no number ahead of 1 is smaller. Sum(1) = 0; Cnt(1) = 0;

    i = 5, a[i] = 6, T = 4;      5 has 2 ahead of it; 3 has 2 ahead of it; 4 has 2 and 3 ahead of it. Sum(6) = Sum(1) + Sum(2) + Sum(3) + Sum(4) + Sum(5) = 1 + 1 + 2 = 4.

    All 1 to 5 can be used as the smallest number in a triplet with 6 being the middle number. Cnt(6) = 5.

    The total count of increasing triplet is the total number of triplets that end on each element, which is 1 + 4 = 5.

    To solve this problem efficiently, we need to achieve the following efficiently.

    1. Get the total count of smaller proceeding numbers(as smallest numbers) of V's smaller proceeding numbers(as middle numbers) in order to get the count of triplets ending at V(as largest number). 

    2. Update the total count of smaller proceeding numbers for V. This will be used later when V is used as a triplet middle number. This is a point update 

    We also need to support the above operations dynamically as we scan the entire array one element at a time.  Binary Indexed Tree fits this purpose perfectly. We can use a BIT bit1 to achieve both operations in log(Max Rating) time. Operation 1 is a range sum query for all numbers' total count of smaller proceeding numbers. Operation 2 is a point update on a number's count of smaller proceeding numbers. To get the count of smaller proceeding numbers of a given number, we need a separate BIT bit2 to perform range query and point update. 

    Essentially, bit1 supports querying/updating the total count of first number of tripets; bit2 supports querying/updating the total count of middle number of triplets. After considering each element V as the last number of a triplet, we then update its smaller proceeding number count in bit2 so V can be used later as a possible middle number in a triplet.

    The runtime is O(N * log (Max Rating)). The max rating is 10^5 so index compression is not needed. If the max rating can go up to 10^7,  we need to apply the compression technique used in the related problem below.

    class Solution {
        private class BinaryIndexedTree {
            int[] ft;
            BinaryIndexedTree(int n) {
                ft = new int[n + 1];
            }
            int rsq(int r) {
                int sum = 0;
                for(; r > 0; r -= (r & (-r))) {
                    sum += ft[r];
                }
                return sum;
            }
            void update(int k, int v) {
                for(; k < ft.length; k += (k & (-k))) {
                    ft[k] += v;
                }
            }
        }
        public int numTeams(int[] rating) {
            int cnt = 0;
            int maxR = 0;
            for(int r : rating) {
                maxR = Math.max(maxR, r);
            }
            cnt += solve(rating, maxR);
            reverse(rating);
            cnt += solve(rating, maxR);
            return cnt;
        }
        private int solve(int[] rating, int n) {
            BinaryIndexedTree bit1 = new BinaryIndexedTree(n);
            BinaryIndexedTree bit2 = new BinaryIndexedTree(n);
            
            int sum = 0;
            for(int i = 0; i < rating.length; i++) {
                sum += bit1.rsq(rating[i] - 1);
                bit1.update(rating[i], bit2.rsq(rating[i] - 1));
                bit2.update(rating[i], 1);
            }
            return sum;
        }
        private void reverse(int[] rating) {
            int i = 0, j = rating.length - 1;
            while(i < j) {
                int temp = rating[i];
                rating[i] = rating[j];
                rating[j] = temp;
                i++;
                j--;
            }
        }
    }

    Related Problem : Moving Points

  • 相关阅读:
    Selenium2+python自动化-使用JS脚本处理网页滚动条
    Python定时框架 Apscheduler 详解【转】
    转:VMware 15 安装 MAC OS 10.13 原版(详细图文教程)
    Appium+Robotframework实现iOS应用的自动化测试
    在jenkins打开roboframework报告:Opening Robot Framework report failed
    springcloud使用pagehelper 实现分页,及total 数据问题
    日志打印request 和response 内容
    springboot+elasticsearch + rabbitMQ实现全文检索(使用transportClient 实现CRUD)
    springboot+elasticsearch + rabbitMQ实现全文检索(springboot+ES整合)
    springboot+elasticsearch + rabbitMQ实现全文检索(项目搭建)
  • 原文地址:https://www.cnblogs.com/lz87/p/12602969.html
Copyright © 2011-2022 走看看