{x}
blog image

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 (i, j, k) 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
  • 3 <= n <= 1000
  • 1 <= rating[i] <= 105
  • All the integers in rating are unique.

1395. Count Number of Teams

This problem asks to count the number of 3-soldier teams that can be formed from a given array of soldier ratings, where a team is valid if the ratings are strictly increasing or strictly decreasing. We'll explore three approaches: enumeration, binary indexed trees, and a recursive approach with memoization.

Solution 1: Enumerate Middle Element (Brute Force)

This approach iterates through each soldier as the middle soldier in a potential team. For each middle soldier, it counts the number of soldiers with smaller ratings to its left and the number of soldiers with larger ratings to its right. The number of teams formed using this middle soldier is the product of these counts (increasing teams) plus the product of soldiers with larger ratings on the left and smaller ratings on the right (decreasing teams).

Time Complexity: O(n^2), due to nested loops iterating through the array. Space Complexity: O(1), constant extra space is used.

Code (Python):

class Solution:
    def numTeams(self, rating: List[int]) -> int:
        ans = 0
        n = len(rating)
        for i in range(n):
            smaller_left = 0
            larger_right = 0
            for j in range(i):
                if rating[j] < rating[i]:
                    smaller_left += 1
            for j in range(i + 1, n):
                if rating[j] > rating[i]:
                    larger_right += 1
            ans += smaller_left * larger_right
 
            larger_left = 0
            smaller_right = 0
            for j in range(i):
                if rating[j] > rating[i]:
                    larger_left += 1
            for j in range(i + 1, n):
                if rating[j] < rating[i]:
                    smaller_right += 1
            ans += larger_left * smaller_right
        return ans
 

Other languages (Java, C++, Go, TypeScript) would follow a similar structure with minor syntax changes.

Solution 2: Binary Indexed Tree (Optimized)

This solution utilizes Binary Indexed Trees (BITs) to optimize the counting process. Two BITs are used: one to track the count of smaller elements to the left, and another to track the count of larger elements to the right. Updating and querying a BIT takes O(log n) time.

Time Complexity: O(n log n), due to iterating through the array and performing BIT operations. Space Complexity: O(n), to store the BITs.

Code (Python):

class BinaryIndexedTree:
    def __init__(self, n):
        self.n = n
        self.bit = [0] * (n + 1)
 
    def update(self, i, val):
        i += 1  # Adjust for 1-based indexing
        while i <= self.n:
            self.bit[i] += val
            i += i & -i
 
    def query(self, i):
        i += 1  # Adjust for 1-based indexing
        res = 0
        while i > 0:
            res += self.bit[i]
            i -= i & -i
        return res
 
 
class Solution:
    def numTeams(self, rating):
        n = len(rating)
        bit_smaller = BinaryIndexedTree(n)
        bit_larger = BinaryIndexedTree(n)
        ans = 0
        for i in range(n):
            smaller_left = bit_smaller.query(rating[i] -1)
            larger_right = bit_larger.query(n-1) - bit_larger.query(rating[i])
            ans += smaller_left* larger_right
            bit_smaller.update(rating[i],1)
            bit_larger.update(rating[i],1)
        return ans
 

Again, adaptations for other languages would be straightforward.

Solution 3: Recursion with Memoization (Less Efficient)

A recursive approach with memoization is possible, but it is generally less efficient than the previous methods for this problem. The recursion explores all possible combinations of three soldiers, but memoization reduces redundant calculations.

Time Complexity: While memoization improves it, the complexity remains high, potentially approaching O(n^3) in the worst case. Space Complexity: O(n^2), to store the memoization table.

Because this approach is significantly less efficient than the previous two for this problem, detailed code is omitted for brevity. The recursive function would explore all subsequences of length 3, checking for increasing or decreasing order. Memoization would store results for subproblems to avoid recalculation.

In summary: The most efficient solution for this problem is using Binary Indexed Trees (Solution 2). Solution 1 (enumeration) is simpler but less efficient for larger input sizes. Solution 3 (recursion with memoization) is generally less efficient for this specific problem. The choice of method depends on the priorities of code readability and performance.