Facebook Pixel

2491. Divide Players Into Teams of Equal Skill

Problem Description

You have an array skill containing positive integers, where skill[i] represents the skill level of the i-th player. The array has an even length n.

Your task is to divide all players into n/2 teams, with each team having exactly 2 players. The requirement is that every team must have the same total skill (sum of the two players' skills).

The chemistry of a team is calculated as the product of the two players' skill values. For example, if a team has players with skills 3 and 5, the chemistry is 3 * 5 = 15.

You need to return the sum of all teams' chemistry values. If it's impossible to form teams where all teams have equal total skill, return -1.

Example: If skill = [3, 2, 5, 1, 3, 4], one valid way to form teams is:

  • Team 1: players with skills 1 and 5 (total = 6, chemistry = 1 * 5 = 5)
  • Team 2: players with skills 2 and 4 (total = 6, chemistry = 2 * 4 = 8)
  • Team 3: players with skills 3 and 3 (total = 6, chemistry = 3 * 3 = 9)

All teams have total skill of 6, so this is valid. The answer would be 5 + 8 + 9 = 22.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Think about what it means for all teams to have the same total skill. If we have players with skills like [1, 2, 3, 4, 5, 6], and we need each pair to sum to the same value, what would that value be?

The key insight is that the sum of all skills is fixed. If we divide players into n/2 teams and each team has the same total skill t, then:

  • Total sum = t * (n/2)
  • Therefore, t = (sum of all skills) / (n/2) = 2 * (sum of all skills) / n

Now, how do we actually form these teams? Consider sorting the array first. After sorting, we have the smallest and largest values at the ends. If we pair the smallest with the largest, the second smallest with the second largest, and so on, we get a pattern where each pair should sum to the same value.

Why does this pairing work? Because if all pairs must sum to the same value t, then:

  • The smallest value must pair with t - smallest
  • The second smallest must pair with t - second_smallest
  • And so on...

Since we need to use all players exactly once, the only way this works is if the smallest pairs with the largest, second smallest with second largest, etc. This is because if the smallest paired with anything other than the largest, we'd eventually run into a situation where we can't form valid pairs with the remaining players.

By sorting and using two pointers (one from the start, one from the end), we can efficiently check if this pairing scheme works. If at any point the sum of the pair doesn't equal our target sum t, we know it's impossible to form valid teams.

Learn more about Two Pointers and Sorting patterns.

Solution Approach

We implement the solution using sorting and two pointers technique:

  1. Sort the array: First, we sort the skill array in ascending order. This allows us to easily pair the smallest with the largest values.

  2. Calculate target sum: After sorting, we determine what the sum of each team should be by adding the first element (smallest) and the last element (largest): t = skill[0] + skill[-1]. This is our target sum that every team must have.

  3. Two-pointer traversal: We initialize two pointers:

    • i = 0 pointing to the beginning of the sorted array
    • j = len(skill) - 1 pointing to the end of the sorted array
  4. Pairing and validation: We iterate while i < j:

    • Check if the current pair sums to our target: skill[i] + skill[j] == t
    • If not equal, it's impossible to form valid teams, so we return -1
    • If equal, we calculate the chemistry of this team: skill[i] * skill[j] and add it to our answer
    • Move the pointers inward: i = i + 1 and j = j - 1
  5. Return result: After successfully pairing all players, we return the sum of all chemistry values.

The algorithm runs in O(n log n) time due to sorting, where n is the length of the skill array. The space complexity is O(1) if we don't count the space used by the sorting algorithm.

This greedy approach works because once we fix the target sum, there's only one way to pair the players: smallest with largest, second smallest with second largest, and so on. Any other pairing would make it impossible to achieve equal sums for all teams.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with skill = [3, 2, 5, 1, 3, 4]:

Step 1: Sort the array

  • Original: [3, 2, 5, 1, 3, 4]
  • Sorted: [1, 2, 3, 3, 4, 5]

Step 2: Calculate target sum

  • The target sum must be: skill[0] + skill[5] = 1 + 5 = 6
  • This means every team must have a total skill of 6

Step 3: Initialize two pointers

  • i = 0 (pointing to 1)
  • j = 5 (pointing to 5)
  • total_chemistry = 0

Step 4: Form teams using two pointers

Iteration 1:

  • Check: skill[0] + skill[5] = 1 + 5 = 6 โœ“ (equals target)
  • Chemistry: 1 ร— 5 = 5
  • Add to total: total_chemistry = 0 + 5 = 5
  • Move pointers: i = 1, j = 4

Iteration 2:

  • Check: skill[1] + skill[4] = 2 + 4 = 6 โœ“ (equals target)
  • Chemistry: 2 ร— 4 = 8
  • Add to total: total_chemistry = 5 + 8 = 13
  • Move pointers: i = 2, j = 3

Iteration 3:

  • Check: skill[2] + skill[3] = 3 + 3 = 6 โœ“ (equals target)
  • Chemistry: 3 ร— 3 = 9
  • Add to total: total_chemistry = 13 + 9 = 22
  • Move pointers: i = 3, j = 2

Step 5: Return result

  • Since i >= j, we've successfully paired all players
  • Return total_chemistry = 22

The teams formed are:

  • Team 1: (1, 5) with chemistry 5
  • Team 2: (2, 4) with chemistry 8
  • Team 3: (3, 3) with chemistry 9

All teams have the same total skill of 6, confirming our solution is valid.

Solution Implementation

1class Solution:
2    def dividePlayers(self, skill: List[int]) -> int:
3        # Sort the skill array to pair smallest with largest
4        skill.sort()
5      
6        # Calculate the target sum for each team (first + last element)
7        target_sum = skill[0] + skill[-1]
8      
9        # Initialize two pointers: left and right
10        left = 0
11        right = len(skill) - 1
12      
13        # Initialize the total chemistry (sum of products)
14        total_chemistry = 0
15      
16        # Pair players from both ends moving towards the center
17        while left < right:
18            # Check if current pair has the required sum
19            if skill[left] + skill[right] != target_sum:
20                # If not equal to target sum, teams cannot be formed equally
21                return -1
22          
23            # Add the chemistry (product) of current pair to total
24            total_chemistry += skill[left] * skill[right]
25          
26            # Move pointers towards the center
27            left += 1
28            right -= 1
29      
30        # Return the total chemistry of all teams
31        return total_chemistry
32
1class Solution {
2    public long dividePlayers(int[] skill) {
3        // Sort the skill array in ascending order
4        Arrays.sort(skill);
5      
6        // Get the length of the array
7        int n = skill.length;
8      
9        // Calculate the target sum for each team (smallest + largest)
10        int targetSum = skill[0] + skill[n - 1];
11      
12        // Initialize the answer to store sum of products
13        long answer = 0;
14      
15        // Use two pointers approach: left pointer from start, right pointer from end
16        for (int left = 0, right = n - 1; left < right; left++, right--) {
17            // Check if current pair has the same sum as target
18            if (skill[left] + skill[right] != targetSum) {
19                // If not equal, it's impossible to divide players equally
20                return -1;
21            }
22          
23            // Add the product of current pair to the answer
24            answer += (long) skill[left] * skill[right];
25        }
26      
27        // Return the total chemistry (sum of products)
28        return answer;
29    }
30}
31
1class Solution {
2public:
3    long long dividePlayers(vector<int>& skill) {
4        // Sort the skill array in ascending order
5        sort(skill.begin(), skill.end());
6      
7        // Get the total number of players
8        int n = skill.size();
9      
10        // Calculate the target sum for each team (using first and last player after sorting)
11        int targetSum = skill[0] + skill[n - 1];
12      
13        // Initialize the total chemistry sum
14        long long totalChemistry = 0;
15      
16        // Use two pointers approach: pair smallest with largest players
17        int left = 0;
18        int right = n - 1;
19      
20        while (left < right) {
21            // Check if current pair has the required sum
22            if (skill[left] + skill[right] != targetSum) {
23                // If not equal to target sum, it's impossible to divide players equally
24                return -1;
25            }
26          
27            // Add the chemistry (product) of current pair to total
28            totalChemistry += static_cast<long long>(skill[left]) * skill[right];
29          
30            // Move pointers inward
31            left++;
32            right--;
33        }
34      
35        return totalChemistry;
36    }
37};
38
1/**
2 * Divides players into teams based on their skill levels.
3 * Each team must have the same total skill level.
4 * Returns the sum of products of skill levels for each team, or -1 if impossible.
5 * 
6 * @param skill - Array of player skill levels
7 * @returns Sum of products of paired players' skills, or -1 if not possible
8 */
9function dividePlayers(skill: number[]): number {
10    const totalPlayers: number = skill.length;
11  
12    // Sort skills in ascending order to pair smallest with largest
13    skill.sort((a: number, b: number) => a - b);
14  
15    // Calculate target sum for each team (first + last element)
16    const targetTeamSum: number = skill[0] + skill[totalPlayers - 1];
17  
18    let totalChemistry: number = 0;
19  
20    // Iterate through first half of array, pairing with corresponding element from end
21    for (let i: number = 0; i < totalPlayers >> 1; i++) {
22        const currentPlayerSkill: number = skill[i];
23        const pairedPlayerSkill: number = skill[totalPlayers - 1 - i];
24      
25        // Check if current pair sums to target
26        if (targetTeamSum !== currentPlayerSkill + pairedPlayerSkill) {
27            return -1;
28        }
29      
30        // Add chemistry (product) of current team to total
31        totalChemistry += currentPlayerSkill * pairedPlayerSkill;
32    }
33  
34    return totalChemistry;
35}
36

Time and Space Complexity

Time Complexity: O(n ร— log n)

The dominant operation in this code is the sorting of the skill array using skill.sort(), which takes O(n ร— log n) time where n is the length of the array. After sorting, the code performs a single pass through the array using two pointers (i and j), which takes O(n/2) = O(n) time. Since O(n ร— log n) + O(n) = O(n ร— log n), the overall time complexity is O(n ร— log n).

Space Complexity: O(log n)

The space complexity comes from the sorting algorithm. Python's sort() method uses Timsort, which requires O(log n) space for the recursion stack in the worst case. The rest of the code uses only a constant amount of extra space for variables (t, i, j, ans), which is O(1). Therefore, the overall space complexity is O(log n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to validate array length is even

While the problem states the array has even length, in real implementations you might encounter odd-length arrays. Not checking this could lead to incorrect results or runtime errors.

Solution:

if len(skill) % 2 != 0:
    return -1

2. Integer overflow in chemistry calculation

When multiplying two skill values, the product might exceed integer limits in languages with fixed integer sizes. In Python this isn't an issue due to arbitrary precision integers, but in languages like Java or C++, this could cause overflow.

Solution:

  • Use appropriate data types (e.g., long long in C++ or long in Java)
  • Check for potential overflow before multiplication

3. Not handling edge cases properly

The most common edge case missed is when the array has only 2 elements. Some implementations might incorrectly assume multiple teams exist.

Solution:

if len(skill) == 2:
    return skill[0] * skill[1]

4. Assuming the target sum without verification

A subtle pitfall is assuming that skill[0] + skill[-1] after sorting will always be the correct target sum without considering that there might be multiple possible sums. However, the algorithm implicitly verifies this by checking all pairs.

5. Modifying the original array unintentionally

Using skill.sort() modifies the original array in-place. If the original array order needs to be preserved elsewhere, this causes issues.

Solution:

skill_sorted = sorted(skill)  # Creates a new sorted list
# Or
skill = skill.copy()
skill.sort()

6. Off-by-one errors in pointer movement

A common mistake is using <= instead of < in the while loop condition, which could process the middle element twice in odd-length arrays (though the problem guarantees even length).

Correct approach:

while left < right:  # Not left <= right
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement priority queue?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More