1733. Minimum Number of People to Teach
Problem Description
You have a social network with m
users where users need a common language to communicate with each other.
Given:
n
: The total number of languages (numbered from 1 to n)languages[i]
: An array where each element is a list of languages that useri
knows (users are 0-indexed in the array but 1-indexed in friendships)friendships[i] = [ui, vi]
: An array of friendship pairs between users
The problem is asking you to find the minimum number of users you need to teach one single language so that all friend pairs can communicate.
Key points to understand:
- Two users can communicate if they share at least one common language
- You can only choose ONE language to teach to selected users
- The goal is to ensure every friendship pair has at least one common language
- Friendships are not transitive (if x is friends with y, and y is friends with z, x and z are not necessarily friends)
For example, if user 1 knows languages [1, 2] and user 2 knows languages [3, 4], and they are friends, they cannot communicate. You would need to teach either language 1, 2, 3, or 4 to at least one of them so they can communicate.
The solution approach involves:
- First identifying which friendship pairs cannot currently communicate (no common language)
- Collecting all users from these "problematic" friendships into a set
- For each language, counting how many of these problematic users already know it
- The answer is the total number of problematic users minus the maximum count from step 3 (choosing the language that the most users already know minimizes teaching)
Intuition
The key insight is that we only need to focus on the friendship pairs that currently cannot communicate - these are the "problematic" pairs where the two users share no common language.
Think about it this way: if two friends already share a language, they can communicate, so we don't need to teach them anything. We only need to worry about pairs that can't communicate.
Once we identify all the users involved in these problematic friendships, we need to choose ONE language to teach to some of them. Which language should we choose?
Here's the clever part: if we choose a language that many of these problematic users already know, we'll need to teach fewer people. For example, if we have 10 problematic users and 7 of them already know language #3, then we only need to teach language #3 to the remaining 3 users.
So our strategy becomes:
- Find all users who are in at least one "cannot communicate" friendship - let's say there are
total
such users - For each language, count how many of these
total
users already know it - Choose the language with the maximum count - let's call this
max_count
- The answer is
total - max_count
because we need to teach this language to everyone who doesn't already know it
This greedy approach works because teaching the most popular language (among the problematic users) minimizes the number of people we need to teach. It's like choosing the most common denominator - we want to leverage what people already know as much as possible.
Learn more about Greedy patterns.
Solution Approach
Let's walk through the implementation step by step:
Step 1: Identify Problematic Friendships
First, we need a helper function check(u, v)
that determines if two users can communicate:
- It iterates through all languages known by user
u
and userv
- If any language matches, they can communicate (returns
True
) - Otherwise, they cannot communicate (returns
False
)
Step 2: Collect Problematic Users
We create a set s
to store all users who are involved in at least one friendship where communication is not possible:
s = set()
for u, v in friendships:
if not check(u, v):
s.add(u)
s.add(v)
Using a set ensures each user is only counted once, even if they're involved in multiple problematic friendships.
Step 3: Count Language Frequencies
For each user in our problematic set s
, we count how many users know each language:
cnt = Counter() for u in s: for l in languages[u - 1]: cnt[l] += 1
Note: We use u - 1
because users are 1-indexed in friendships but 0-indexed in the languages array.
Step 4: Calculate the Answer
The minimum number of users to teach is:
return len(s) - max(cnt.values(), default=0)
len(s)
is the total number of problematic usersmax(cnt.values())
is the maximum count of any language among these users- The difference gives us the minimum number of users who need to learn the most popular language
- We use
default=0
to handle the edge case where all friendships can already communicate (empty sets
)
This greedy approach ensures we teach the fewest possible users by selecting the language that the most problematic users already know.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Given:
n = 3
(languages numbered 1, 2, 3)languages = [[1], [2], [1,2], [3]]
- User 1 knows: language 1
- User 2 knows: language 2
- User 3 knows: languages 1 and 2
- User 4 knows: language 3
friendships = [[1,2], [2,4], [3,4]]
Step 1: Identify Problematic Friendships
Let's check each friendship pair:
- Friendship [1,2]: User 1 knows {1}, User 2 knows {2} β No common language β
- Friendship [2,4]: User 2 knows {2}, User 4 knows {3} β No common language β
- Friendship [3,4]: User 3 knows {1,2}, User 4 knows {3} β No common language β
All three friendships are problematic!
Step 2: Collect Problematic Users
From the problematic friendships, we collect all involved users:
- From [1,2]: Add users 1 and 2
- From [2,4]: Add users 2 and 4 (user 2 already added)
- From [3,4]: Add users 3 and 4 (user 4 already added)
Problematic users set s = {1, 2, 3, 4}
(all 4 users!)
Step 3: Count Language Frequencies
Count how many problematic users know each language:
- Language 1: User 1 knows it, User 3 knows it β count = 2
- Language 2: User 2 knows it, User 3 knows it β count = 2
- Language 3: User 4 knows it β count = 1
Language frequency: {1: 2, 2: 2, 3: 1}
Step 4: Calculate the Answer
- Total problematic users = 4
- Maximum language count = max(2, 2, 1) = 2
- Users to teach = 4 - 2 = 2
Verification:
If we choose language 1 (known by 2 users already):
- Teach language 1 to users 2 and 4
- Now: [1,2] can communicate via language 1 β
- Now: [2,4] can communicate via language 1 β
- Now: [3,4] can communicate via language 1 β
Similarly, if we choose language 2, we'd teach it to users 1 and 4. Either way, we need to teach exactly 2 users!
Solution Implementation
1class Solution:
2 def minimumTeachings(
3 self, n: int, languages: List[List[int]], friendships: List[List[int]]
4 ) -> int:
5 """
6 Find the minimum number of users who need to learn a new language
7 so that all friend pairs can communicate.
8
9 Args:
10 n: Total number of available languages (1 to n)
11 languages: List where languages[i] contains languages known by user i+1
12 friendships: List of [user1, user2] pairs representing friendships
13
14 Returns:
15 Minimum number of users who need to learn a new language
16 """
17
18 def can_communicate(user1: int, user2: int) -> bool:
19 """
20 Check if two users can communicate (share at least one common language).
21
22 Args:
23 user1: First user ID (1-indexed)
24 user2: Second user ID (1-indexed)
25
26 Returns:
27 True if users share a common language, False otherwise
28 """
29 # Get languages for both users (convert to 0-indexed)
30 user1_languages = languages[user1 - 1]
31 user2_languages = languages[user2 - 1]
32
33 # Check if any language is common between the two users
34 for lang1 in user1_languages:
35 for lang2 in user2_languages:
36 if lang1 == lang2:
37 return True
38 return False
39
40 # Find all users who are in friendships that cannot communicate
41 users_needing_language = set()
42 for user1, user2 in friendships:
43 if not can_communicate(user1, user2):
44 # Both users in this friendship need to learn a common language
45 users_needing_language.add(user1)
46 users_needing_language.add(user2)
47
48 # Count how many problematic users already know each language
49 language_count = Counter()
50 for user in users_needing_language:
51 # For each user who needs to communicate, count their known languages
52 for language_id in languages[user - 1]:
53 language_count[language_id] += 1
54
55 # The minimum teachings needed is:
56 # Total problematic users - Maximum users who already know the same language
57 # If we teach the most commonly known language to those who don't know it,
58 # we minimize the number of teachings
59 if language_count:
60 return len(users_needing_language) - max(language_count.values())
61 else:
62 # If no problematic users exist, no teaching is needed
63 return 0
64
1class Solution {
2 /**
3 * Finds the minimum number of people to teach a new language so that
4 * all friendships can communicate.
5 *
6 * @param n The total number of languages available (1 to n)
7 * @param languages 2D array where languages[i] contains languages known by person i+1
8 * @param friendships 2D array where each element [u, v] represents a friendship
9 * @return The minimum number of people that need to be taught a new language
10 */
11 public int minimumTeachings(int n, int[][] languages, int[][] friendships) {
12 // Set to store people who cannot communicate with at least one friend
13 Set<Integer> peopleNeedingHelp = new HashSet<>();
14
15 // Check each friendship pair to find those who cannot communicate
16 for (int[] friendship : friendships) {
17 int person1 = friendship[0];
18 int person2 = friendship[1];
19
20 // If these two friends don't share a common language
21 if (!haveCommonLanguage(person1, person2, languages)) {
22 peopleNeedingHelp.add(person1);
23 peopleNeedingHelp.add(person2);
24 }
25 }
26
27 // If everyone can already communicate, no teaching needed
28 if (peopleNeedingHelp.isEmpty()) {
29 return 0;
30 }
31
32 // Count how many people needing help already know each language
33 int[] languageCount = new int[n + 1]; // Index 0 unused, languages are 1 to n
34
35 for (int person : peopleNeedingHelp) {
36 // For each language this person knows
37 for (int language : languages[person - 1]) { // person-1 because array is 0-indexed
38 languageCount[language]++;
39 }
40 }
41
42 // Find the language known by the most people who need help
43 int maxPeopleKnowingLanguage = 0;
44 for (int count : languageCount) {
45 maxPeopleKnowingLanguage = Math.max(maxPeopleKnowingLanguage, count);
46 }
47
48 // The minimum people to teach = total people needing help - those who already know the best language
49 return peopleNeedingHelp.size() - maxPeopleKnowingLanguage;
50 }
51
52 /**
53 * Checks if two people share at least one common language.
54 *
55 * @param person1 First person (1-indexed)
56 * @param person2 Second person (1-indexed)
57 * @param languages 2D array of languages known by each person
58 * @return true if they share a common language, false otherwise
59 */
60 private boolean haveCommonLanguage(int person1, int person2, int[][] languages) {
61 // Check each language of person1 against each language of person2
62 for (int language1 : languages[person1 - 1]) { // person1-1 for 0-indexed array
63 for (int language2 : languages[person2 - 1]) { // person2-1 for 0-indexed array
64 if (language1 == language2) {
65 return true; // Found a common language
66 }
67 }
68 }
69 return false; // No common language found
70 }
71}
72
1class Solution {
2public:
3 int minimumTeachings(int n, vector<vector<int>>& languages, vector<vector<int>>& friendships) {
4 // Set to store users who cannot communicate with at least one friend
5 unordered_set<int> usersNeedingHelp;
6
7 // Check each friendship pair
8 for (auto& friendship : friendships) {
9 int user1 = friendship[0];
10 int user2 = friendship[1];
11
12 // If these two users don't share a common language, add both to the set
13 if (!haveCommonLanguage(user1, user2, languages)) {
14 usersNeedingHelp.insert(user1);
15 usersNeedingHelp.insert(user2);
16 }
17 }
18
19 // If all friends can already communicate, no teaching needed
20 if (usersNeedingHelp.empty()) {
21 return 0;
22 }
23
24 // Count how many users needing help already know each language
25 vector<int> languageCount(n + 1, 0); // languageCount[i] = number of users who know language i
26
27 for (int userId : usersNeedingHelp) {
28 // For each user needing help, count the languages they already know
29 for (int& languageId : languages[userId - 1]) { // userId - 1 because users are 1-indexed
30 ++languageCount[languageId];
31 }
32 }
33
34 // Find the language known by the most users who need help
35 int maxUsersKnowingLanguage = *max_element(languageCount.begin(), languageCount.end());
36
37 // Minimum teachings = total users needing help - users who already know the most popular language
38 return usersNeedingHelp.size() - maxUsersKnowingLanguage;
39 }
40
41private:
42 // Helper function to check if two users share at least one common language
43 bool haveCommonLanguage(int user1, int user2, vector<vector<int>>& languages) {
44 // Check if any language of user1 matches any language of user2
45 for (int language1 : languages[user1 - 1]) { // user1 - 1 because users are 1-indexed
46 for (int language2 : languages[user2 - 1]) { // user2 - 1 because users are 1-indexed
47 if (language1 == language2) {
48 return true; // Found a common language
49 }
50 }
51 }
52 return false; // No common language found
53 }
54};
55
1/**
2 * Finds the minimum number of users that need to be taught a new language
3 * so that all friend pairs can communicate
4 * @param n - Total number of languages available (1 to n)
5 * @param languages - Array where languages[i] contains the languages known by user i+1
6 * @param friendships - Array of friend pairs [user1, user2]
7 * @returns Minimum number of users that need to learn a new language
8 */
9function minimumTeachings(n: number, languages: number[][], friendships: number[][]): number {
10 // Set to store users who cannot communicate with at least one friend
11 const usersNeedingHelp = new Set<number>();
12
13 // Check each friendship pair to identify users who cannot communicate
14 for (const friendship of friendships) {
15 const user1 = friendship[0];
16 const user2 = friendship[1];
17
18 // If these two users don't share a common language, add both to the set
19 if (!haveCommonLanguage(user1, user2, languages)) {
20 usersNeedingHelp.add(user1);
21 usersNeedingHelp.add(user2);
22 }
23 }
24
25 // If all friends can already communicate, no teaching needed
26 if (usersNeedingHelp.size === 0) {
27 return 0;
28 }
29
30 // Count how many users needing help already know each language
31 // languageCount[i] = number of users who know language i
32 const languageCount = new Array(n + 1).fill(0);
33
34 // For each user needing help, count the languages they already know
35 for (const userId of usersNeedingHelp) {
36 // Iterate through languages known by this user (userId - 1 because users are 1-indexed)
37 for (const languageId of languages[userId - 1]) {
38 languageCount[languageId]++;
39 }
40 }
41
42 // Find the language known by the most users who need help
43 const maxUsersKnowingLanguage = Math.max(...languageCount);
44
45 // Minimum teachings = total users needing help - users who already know the most popular language
46 return usersNeedingHelp.size - maxUsersKnowingLanguage;
47}
48
49/**
50 * Helper function to check if two users share at least one common language
51 * @param user1 - First user ID (1-indexed)
52 * @param user2 - Second user ID (1-indexed)
53 * @param languages - Array where languages[i] contains the languages known by user i+1
54 * @returns true if users share at least one common language, false otherwise
55 */
56function haveCommonLanguage(user1: number, user2: number, languages: number[][]): boolean {
57 // Get languages known by each user (subtract 1 because users are 1-indexed)
58 const languages1 = languages[user1 - 1];
59 const languages2 = languages[user2 - 1];
60
61 // Check if any language of user1 matches any language of user2
62 for (const language1 of languages1) {
63 for (const language2 of languages2) {
64 if (language1 === language2) {
65 return true; // Found a common language
66 }
67 }
68 }
69
70 return false; // No common language found
71}
72
Time and Space Complexity
Time Complexity: O(k Γ mΒ² + k Γ m)
Breaking down the analysis:
- The
check
function has nested loops that iterate through languages of two users, which takesO(mΒ²)
in the worst case wherem
is the maximum number of languages a person can know - The first main loop iterates through all friendships (
k
iterations) and callscheck
for each, resulting inO(k Γ mΒ²)
- Building set
s
takesO(k)
in the worst case - The second loop iterates through users in set
s
(at most2k
users) and their languages (at mostm
each), contributingO(k Γ m)
- Finding the maximum value in the counter takes
O(n)
wheren
is the total number of languages - Overall:
O(k Γ mΒ² + k Γ m + n)
, which simplifies toO(k Γ mΒ²)
as the dominant term
Space Complexity: O(k + n)
- Set
s
stores at most2k
users (two per friendship):O(k)
- Counter
cnt
stores at mostn
language counts:O(n)
- Total space:
O(k + n)
Note: The reference answer states O(mΒ² Γ k)
, which appears to be the same as my analysis O(k Γ mΒ²)
with the terms reordered.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Checking All Languages Instead of Just Problematic Users
A common mistake is to count language frequencies across ALL users in the network, rather than just the users involved in problematic friendships.
Incorrect approach:
# WRONG: Counting languages for all users
language_count = Counter()
for i, user_languages in enumerate(languages):
for lang in user_languages:
language_count[lang] += 1
Why it's wrong: We only need to teach languages to users who are in friendships that cannot communicate. Teaching a language to users who can already communicate with all their friends is unnecessary.
Correct approach:
# RIGHT: Only count languages for users in problematic friendships language_count = Counter() for user in users_needing_language: # Only problematic users for language_id in languages[user - 1]: language_count[language_id] += 1
2. Index Confusion Between User IDs and Array Indices
Users are 1-indexed in the friendships array but the languages array is 0-indexed. Forgetting to convert between these can cause index errors or incorrect results.
Incorrect approach:
# WRONG: Using user ID directly as array index for user in users_needing_language: for language_id in languages[user]: # Missing the -1 adjustment language_count[language_id] += 1
Correct approach:
# RIGHT: Convert 1-indexed user ID to 0-indexed array position for user in users_needing_language: for language_id in languages[user - 1]: # Proper index adjustment language_count[language_id] += 1
3. Trying to Optimize Language Selection Per Friendship Pair
Some might try to find the optimal language for each friendship pair individually, but the problem requires choosing ONE single language to teach globally.
Incorrect approach:
# WRONG: Trying to solve each friendship independently total_teachings = 0 for u, v in friendships: if not can_communicate(u, v): # Find best language for this pair only best_for_pair = find_best_language(u, v) total_teachings += teach_language(best_for_pair, u, v)
Correct approach: The solution must choose one language that works best globally for all problematic friendships, not optimize each friendship individually.
4. Using Set Operations Incorrectly for Language Comparison
When checking if users can communicate, using set operations without proper conversion can lead to errors.
Potentially problematic approach:
# May be inefficient or error-prone with certain inputs
def can_communicate(user1, user2):
return bool(set(languages[user1-1]) & set(languages[user2-1]))
While this works, it creates unnecessary set objects for every check. The nested loop approach in the solution is clearer and avoids repeated set creation for frequently checked users.
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!