ProblemPermalink
Problem ID: 1511
Title: Count Number of Teams
Difficulty: Medium
Description:
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.length3 <= n <= 10001 <= rating[i] <= 105- All the integers in
ratingare unique.
ThoughtsPermalink
As usual, I didn’t take a step back to think about the most optimal solution before attempting to solve this problem. The initial approach I took was the usual DP counting problem where the number of ways to form a team of 2 is add the number of ways soldier i can join a team of 1 and the number of ways to form a team of 3 is to add the number of ways soldier i can join a team of 2.
Initial solution (O(N^2)):
class Solution {
public:
int helper(const vector<int>& rating) {
const auto n = rating.size();
vector<int> dp(n,1);
for (size_t soldier = 1; soldier < 3; soldier++) {
vector<int> next_dp(n, 0);
for (size_t j = 0; j < n; j++) {
for (size_t i = 0; i < j; i++) {
if (rating[i] >= rating[j]) continue;
next_dp[j] += dp[i];
}
}
dp = move(next_dp);
}
int ret = 0;
for (const auto count : dp) {
ret += count;
}
return ret;
}
int numTeams(vector<int>& rating) {
int forward = helper(rating);
reverse(rating.begin(), rating.end());
int backward = helper(rating);
return forward + backward;
}
};
SolutionPermalink
General Idea
Intuition: Treat each solider as the middle j solider and find the number of possible of i (left) and number of possible k (right).
To find the number of possible i and k:
- Two multiset (sorted)
- One multiset represents the soldiers to the left of
j - One represents the soldiers to the right of
j.
- One multiset represents the soldiers to the left of
- Finding the number of possible
iis the number of elements in the left multiset that are strictly less thanrating[j]- This can be easily achieved by using
lower_bound(the first element that>= rating[j]) and moving it back by1 - Use
std::distanceto find the distance between the start and the new iterator
- This can be easily achieved by using
- Finding the number of possible
jis the number of elements in the right multiset that are strictly greater thanrating[j]- This can be easily achieved by using
upper_bound(the first element that> rating[j]) - Use
std::distanceto find the distance between the new iterator and the end
- This can be easily achieved by using
- Complexity: O(log(N))
Multiplying the number of possible i and k will give the total groups with solider j in the middle.
ImplementationPermalink
class Solution {
public:
int solve(const vector<int>& ratings) {
const auto n = ratings.size();
multiset<int> left;
multiset<int> right;
for (const auto rating : ratings) {
right.insert(rating);
}
int ret = 0;
for (size_t i = 0; i < n; i++) {
auto rating = ratings[i];
right.erase(right.find(rating));
auto l_it = lower_bound(left.begin(), left.end(), rating);
if (l_it != left.begin()) {
// the largest left that is strictly less than rating
l_it--;
auto left_count = distance(left.begin(), l_it) +1;
// the smallest right that is strictly more than rating
auto r_it = upper_bound(right.begin(), right.end(), rating);
auto right_count = distance(r_it, right.end());
ret += (left_count*right_count);
}
left.insert(rating);
}
return ret;
}
int numTeams(vector<int>& rating) {
int forward = solve(rating);
reverse(rating.begin(), rating.end());
int backward = solve(rating);
return forward + backward;
}
};