Skip to content

Commit e9c2e9c

Browse files
My solution to 1395
1 parent 80bae02 commit e9c2e9c

File tree

1 file changed

+101
-2
lines changed

1 file changed

+101
-2
lines changed

problems/1395/jeremymanning.md

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,110 @@
11
# [Problem 1395: Count Number of Teams](https://leetcode.com/problems/count-number-of-teams/description/?envType=daily-question)
22

33
## Initial thoughts (stream-of-consciousness)
4+
- Enumerating every *possible* team will take $O(n^3)$ time. That seems long.
5+
- I think we could do this using two sets of queues (BFS) or stacks (DFS)-- but let's try queues:
6+
- Initialize `nTeams = 0`.
7+
- One queue stores *increasing* teams and the other stores *decreasing* teams
8+
- First, enqueue each soldier in both queues as a single item list of indices
9+
- Next, for each queue type, loop until the queue is empty:
10+
- Dequeue the next list (at the front of the queue). Let's say the last item in the list has index $i$.
11+
- For indices $i + 1$ to the end of the list of soldiers, add them to the teams if they satisfy the correct ordering. E.g., for the increasing queue, ensure that the next item (at index $j$) is greater than the current last item at index $i$. If the new team has length 3, increment `nTeams`. Otherwise enqueue any newly formed "valid" teams.
12+
- Finally, return `nTeams`.
413

514
## Refining the problem, round 2 thoughts
15+
- Any edge cases to consider?
16+
- If there are fewer than 3 soldiers, no teams are possible...but we're given that $3 \leq n \leq 1000$, so we don't need to consider this case
17+
- Strange `rating` values? We're given that `ratings` are all integers.
18+
- We're also told that the integers in `ratings` are unique-- we could probably leverage this to implement some sort of sorting-based solution 🤔...but let's see if the queue idea works first.
19+
- I think we can try this...
620

721
## Attempted solution(s)
822
```python
9-
class Solution: # paste your code here!
10-
...
23+
from collections import deque
24+
25+
class Solution:
26+
def numTeams(self, rating: List[int]) -> int:
27+
nTeams = 0
28+
increasing_queue = deque([[i] for i in range(len(rating))])
29+
decreasing_queue = increasing_queue.copy()
30+
31+
# look for increasing teams (where rating[i] < rating[j] < rating[k])
32+
while len(increasing_queue) > 0:
33+
next_team = increasing_queue.popleft()
34+
for i in range(next_team[-1], len(rating)):
35+
if rating[i] > rating[next_team[-1]]:
36+
if len(next_team) == 2:
37+
nTeams += 1
38+
else:
39+
new_team = next_team.copy()
40+
new_team.append(i)
41+
increasing_queue.append(new_team)
42+
43+
# now look for decreasing teams (where rating[i] > rating[j] > rating[k])
44+
while len(decreasing_queue) > 0:
45+
next_team = decreasing_queue.popleft()
46+
for i in range(next_team[-1], len(rating)):
47+
if rating[i] < rating[next_team[-1]]:
48+
if len(next_team) == 2:
49+
nTeams += 1
50+
else:
51+
new_team = next_team.copy()
52+
new_team.append(i)
53+
decreasing_queue.append(new_team)
54+
55+
return nTeams
56+
```
57+
- Given test cases pass
58+
- New test cases:
59+
- `rating = [4, 7, 1, 9, 10, 14, 3, 29, 1000, 950, 26, 44]`: pass
60+
- `rating = [62, 2, 35, 32, 4, 75, 48, 38, 28, 92, 8, 100, 68, 95, 63, 40, 42, 21, 47, 43, 89, 79, 14, 58, 85, 80, 15, 41, 10, 37, 30, 31, 24, 1, 23, 45, 53, 83, 65, 3, 49, 66, 6, 54, 34, 72, 29, 71, 52, 81, 98, 82, 18, 36, 88, 20, 12, 99, 22, 77, 97, 60, 17, 61, 27, 16, 76, 33, 69, 51, 19, 25, 46, 39, 74, 94, 67, 55, 96, 90, 93, 64, 26, 73, 87, 91, 78, 11, 5, 9, 57, 56, 50, 70, 44, 84, 86, 59, 13, 7]`: pass (note: this is a random permutation of the numbers 1...100)
61+
- Seems ok; submitting...
62+
63+
![Screenshot 2024-07-28 at 11 40 23 PM](https://github.com/user-attachments/assets/40a889c6-af6e-4574-9cca-deb374176779)
64+
65+
Uh oh...time limit exceeded! So: the algorithm seems correct (and even for this "failed" test case we get the right answer if I run it through "use as test case"). But this algorithm is still $O(n^3)$, which is apparently not good enough. Let's go back to the drawing board...
66+
67+
## Revised thoughts (back to stream of consciousness...)
68+
- I wonder if the sorting idea might work...or...hmmm... 🤔
69+
- The algorithm I implemented also requires us to re-compare elements several times. E.g., suppose we're considering building a team that includes index $i$. If we want to build an increasing team, then *any* item from index $0...(i - 1)$ that is less than `ratings[i]` could serve as the first element, and any item from index $(i + 1)...n$ that is greater than `ratings[i]` could serve as the third element. So if there are $a$ elements less than `ratings[i]` with indices less than `i` and $b$ elements greater than `ratings[i]` with indices greater than `i`, the total number of increasing teams is $ab$. We can use an analogous approach to track the number of decreasing teams (just flipping the greater than/less than signs).
70+
- Let's try something like:
71+
- For each element in turn (at index `i`):
72+
- track how many items to the left are bigger/smaller, and how many items to the right are bigger/smaller
73+
- the number of increasing teams we can make with element `i` as the second team member is `left_smaller * right_bigger + left_bigger * right_smaller`
74+
75+
New solution:
76+
```python
77+
class Solution:
78+
def numTeams(self, rating: List[int]) -> int:
79+
nTeams = 0
80+
for i in range(len(rating)):
81+
left_smaller, left_bigger, right_smaller, right_bigger = 0, 0, 0, 0
82+
for j in range(i): # j is to the left of i
83+
if rating[i] > rating[j]:
84+
left_smaller += 1
85+
else:
86+
left_bigger += 1
87+
88+
for j in range(i + 1, len(rating)): # j is to the right of i
89+
if rating[i] > rating[j]:
90+
right_smaller += 1
91+
else:
92+
right_bigger += 1
93+
94+
nTeams += left_smaller * right_bigger + left_bigger * right_smaller
95+
96+
return nTeams
1197
```
98+
- Ok: all of the test cases, including the ones that exceeded the time limit previously are now passing
99+
- With this algorithm I think we've gotten the runtime down to $O(n^2)$ (for filling in `left_smaller`, `left_bigger`, `right_smaller`, and `right_bigger` inside the main loop; the lefts and rights each take $O(n)$ steps to fill in, and those loops run for each element in turn (of which there are $n$).
100+
- Submitting...
101+
102+
![Screenshot 2024-07-29 at 12 33 40 AM](https://github.com/user-attachments/assets/94653f0e-cce9-47d7-b43a-62192dfb2679)
103+
104+
🎉!
105+
106+
107+
108+
109+
110+

0 commit comments

Comments
 (0)