title: Best Team With No Conflicts
slug: best-team-with-no-conflicts
difficulty: medium
leetcode_id: 1626
leetcode_url: https://leetcode.com/problems/best-team-with-no-conflicts/
categories:
  - arrays
  - dynamic-programming
  - sorting
patterns:
  - dynamic-programming

description: |
  You are the manager of a basketball team. For the upcoming tournament, you want to choose the team with the highest overall score. The score of the team is the **sum** of scores of all the players in the team.

  However, the basketball team is not allowed to have **conflicts**. A **conflict** exists if a younger player has a **strictly higher** score than an older player. A conflict does **not** occur between players of the same age.

  Given two lists, `scores` and `ages`, where each `scores[i]` and `ages[i]` represents the score and age of the i<sup>th</sup> player, respectively, return *the highest overall score of all possible basketball teams*.

constraints: |
  - `1 <= scores.length, ages.length <= 1000`
  - `scores.length == ages.length`
  - `1 <= scores[i] <= 10^6`
  - `1 <= ages[i] <= 1000`

examples:
  - input: "scores = [1,3,5,10,15], ages = [1,2,3,4,5]"
    output: "34"
    explanation: "You can choose all the players. Since ages are strictly increasing and scores are also strictly increasing, no conflicts exist."
  - input: "scores = [4,5,6,5], ages = [2,1,2,1]"
    output: "16"
    explanation: "It is best to choose the last 3 players (scores 5, 6, 5 with ages 1, 2, 1). Notice that you are allowed to choose multiple people of the same age."
  - input: "scores = [1,2,3,5], ages = [8,9,10,1]"
    output: "6"
    explanation: "It is best to choose the first 3 players (scores 1, 2, 3). The player with score 5 and age 1 cannot be included because they are younger but have a higher score than the others."

explanation:
  intuition: |
    Imagine you're sorting players into a roster where **seniority must be respected** — older players shouldn't be outscored by younger ones.

    The key insight is that this is a **variant of the Longest Increasing Subsequence (LIS) problem**, but instead of finding the longest subsequence, we want the one with the **maximum sum**.

    Think of it this way: if we sort all players by age (and by score as a tiebreaker for same-age players), then we need to find a subsequence where scores are **non-decreasing**. Why? Because after sorting by age, any player we pick later is at least as old. If their score is also at least as high, there's no conflict.

    This transformation is powerful — it converts a 2D constraint (age AND score) into a 1D problem (just scores, after sorting).

  approach: |
    We use **Dynamic Programming** after sorting the players:

    **Step 1: Combine and sort players**

    - Create pairs of `(age, score)` for each player
    - Sort by age first, then by score (both ascending)
    - This ensures when we process player `j` after player `i`, player `j` is at least as old

    &nbsp;

    **Step 2: Define the DP state**

    - `dp[i]`: Maximum total score of a valid team ending with the i<sup>th</sup> player (after sorting)
    - Base case: Each player alone forms a valid team, so `dp[i] = score[i]` initially

    &nbsp;

    **Step 3: Fill the DP table**

    - For each player `i`, look at all previous players `j` where `j < i`
    - If `score[j] <= score[i]`, player `j` can be on the same team as player `i` (no conflict)
    - Update: `dp[i] = max(dp[i], dp[j] + score[i])`

    &nbsp;

    **Step 4: Return the answer**

    - The answer is `max(dp)` — the best team might end at any player

    &nbsp;

    The sorting step is crucial: it ensures that when comparing players `j` and `i` (with `j < i`), player `j` is no older than player `i`. Combined with checking `score[j] <= score[i]`, we guarantee no conflicts.

  common_pitfalls:
    - title: Forgetting to Sort by Score as Tiebreaker
      description: |
        When two players have the same age, their relative order matters. If you only sort by age, you might miss valid combinations.

        For example, with ages `[2, 2]` and scores `[5, 3]`, sorting only by age could give either order. If you process the player with score 5 first and then score 3, you'd incorrectly think you can't include both (since 3 < 5 but same age means no conflict!).

        By sorting by score as a secondary key, players of the same age are ordered by score, ensuring the DP comparison works correctly.
      wrong_approach: "Sort by age only"
      correct_approach: "Sort by (age, score) to handle same-age players correctly"

    - title: Looking for Longest Subsequence Instead of Maximum Sum
      description: |
        This problem looks like LIS but the goal is different. In LIS, we count elements. Here, we sum scores.

        If you use LIS logic directly (`dp[i] = max length`), you'll get the team with the most players, not the highest score. A team of 2 high-scoring players can beat a team of 5 low-scoring players.
      wrong_approach: "Count players in valid subsequence"
      correct_approach: "Sum scores in valid subsequence"

    - title: Misunderstanding the Conflict Rule
      description: |
        A conflict only exists when a **younger** player has a **strictly higher** score. Same age never causes conflicts, and equal or lower scores don't cause conflicts.

        Be careful with the inequality: it's `score[younger] > score[older]` that's forbidden, not `>=`.
      wrong_approach: "Treating equal scores as conflicts"
      correct_approach: "Only strictly higher scores from younger players cause conflicts"

  key_takeaways:
    - "**Reduce dimensions via sorting**: Sorting by one attribute lets you focus on the other, turning 2D constraints into 1D problems"
    - "**LIS variant pattern**: Many problems involve finding optimal subsequences with constraints — recognize when LIS-style DP applies"
    - "**Secondary sort keys matter**: When primary keys are equal, the secondary sort order affects correctness"
    - "**Sum vs count**: This is maximum sum subsequence, not longest — the DP state tracks cumulative value, not length"

  time_complexity: "O(n^2). After O(n log n) sorting, we have nested loops: for each of the n players, we check all previous players."
  space_complexity: "O(n). We store the sorted player list and the DP array, both of size n."

solutions:
  - approach_name: Dynamic Programming with Sorting
    is_optimal: true
    code: |
      def best_team_score(scores: list[int], ages: list[int]) -> int:
          n = len(scores)
          # Combine age and score, sort by age then score
          players = sorted(zip(ages, scores))

          # dp[i] = max score of valid team ending with player i
          dp = [0] * n

          for i in range(n):
              # Player i alone is a valid team
              dp[i] = players[i][1]  # score of player i

              # Try extending from all previous players
              for j in range(i):
                  # No conflict if previous player's score <= current score
                  # (age is already guaranteed: players[j].age <= players[i].age)
                  if players[j][1] <= players[i][1]:
                      dp[i] = max(dp[i], dp[j] + players[i][1])

          # Best team could end at any player
          return max(dp)
    explanation: |
      **Time Complexity:** O(n^2) — Nested loops over n players after sorting.

      **Space Complexity:** O(n) — Storage for sorted players and DP array.

      By sorting players by (age, score), we ensure that when we process player `i`, all previous players `j` are no older. The conflict condition simplifies to checking if `score[j] <= score[i]`. We track the maximum sum achievable ending at each player, then return the global maximum.

  - approach_name: Brute Force (Exponential)
    is_optimal: false
    code: |
      def best_team_score(scores: list[int], ages: list[int]) -> int:
          n = len(scores)
          max_score = 0

          # Try all 2^n subsets
          for mask in range(1, 1 << n):
              team_scores = []
              team_ages = []

              # Extract players in this subset
              for i in range(n):
                  if mask & (1 << i):
                      team_scores.append(scores[i])
                      team_ages.append(ages[i])

              # Check for conflicts
              valid = True
              for i in range(len(team_ages)):
                  for j in range(len(team_ages)):
                      if team_ages[i] < team_ages[j] and team_scores[i] > team_scores[j]:
                          valid = False
                          break
                  if not valid:
                      break

              if valid:
                  max_score = max(max_score, sum(team_scores))

          return max_score
    explanation: |
      **Time Complexity:** O(2^n * n^2) — Enumerate all subsets, check each for conflicts.

      **Space Complexity:** O(n) — Storage for current team being checked.

      This approach tries every possible team composition and validates each one. With n up to 1000, this is completely infeasible (2^1000 subsets). Included to illustrate why the DP approach is necessary.
