Sale: Get 60% Off on all Pro Plans. Buy Now

Best Sightseeing Pair

Problem

Given an array of integers values, return the maximum score of a sightseeing pair, where the score of a pair (i, j) is values[i] + values[j] + i - j and i < j.

  • 2 ≤ values.length ≤ 5 * 10⁴
  • 1 ≤ values[i] ≤ 1000

Example

Input: values = [8,1,5,2,6]
Output: 11

The brute-force approach would check all pairs (i, j) and compute values[i] + values[j] + i - j, which is O(n^2). For example, the pair (0, 2) yields 8 + 5 + 0 - 2 = 11, which is the maximum score. The key insight is to rewrite the score as (values[i] + i) + (values[j] - j), allowing a linear scan to track the best values[i] + i seen so far and combine it with values[j] - j at each step.

Approach

Straightforward Solution

A brute-force solution checks all pairs (i, j) with i < j, computing the score each time, resulting in O(n^2) time complexity, which is too slow for large inputs.

Core Observation

The score formula values[i] + values[j] + i - j can be rearranged as (values[i] + i) + (values[j] - j). This separates the problem into two parts: a prefix term and a suffix term, enabling a greedy approach.

Path to Optimal

Preview

By rewriting the score as (values[i] + i) + (values[j] - j), the problem reduces to finding, for each j, the maximum values[i] + i for i < j…

Full step-by-step walkthrough on Pro

Optimal Approach

Preview

Iterate through the array from left to right, maintaining the maximum prefix value (values[i] + i) seen so far. For each index j, compute the candidate score as best_left + values[j] - j, update the global maximum, and then update best_left with max(best_left, values[j] + j)…

Full step-by-step walkthrough on Pro

Want the full reasoning chain?

Unlock the complete walkthrough, line-by-line analysis, and recall drill.

Unlock Pro

Time

O(n)

The algorithm iterates through the array once, updating the maximum prefix and global maximum score in constant time per element.

Space

O(1)

Only a few variables are used to track the best prefix and the global maximum score, independent of input size.

Pattern Spotlight

Greedy Algorithms (Prefix Tracking)

When a problem's scoring function can be decomposed into a sum of a prefix-dependent term and a suffix-dependent term, maintain the best prefix value while iterating to efficiently compute the maximum combined score in a single pass.

Solution

Python
1class Solution:
2 def maxScoreSightseeingPair(self, values: list[int]) -> int:
3 best_left = values[0]
4 best = float("-inf")
5
6 for j in range(1, len(values)):
7 best = max(best, best_left + values[j] - j)
8 best_left = max(best_left, values[j] + j)
9
10 return best

Step-by-Step Solution

1

Track Maximum Prefix Value and Update Global Maximum Score

3best_left = values[0]
4best = float("-inf")
6for j in range(1, len(values)):
7 best = max(best, best_left + values[j] - j)
8 best_left = max(best_left, values[j] + j)
10return best

Objective

To maintain the best prefix value (values[i] + i) and update the maximum sightseeing pair score in a single pass.

Key Insight

By keeping track of the maximum values[i] + i encountered so far, the algorithm can efficiently compute the score for each j as best_left + values[j] - j. Updating best_left with max(best_left, values[j] + j) ensures the prefix value is always optimal for future iterations. This approach transforms a quadratic problem into a linear one by exploiting the problem's additive structure.

Interview Quick-Check

Core Logic

Maintain a running maximum of values[i] + i to represent the best prefix, and for each j, compute the candidate score as best_left + values[j] - j, updating the global maximum accordingly.

State & Boundaries

Start best_left with values[0] because the first element is the initial prefix candidate; iterate j from 1 to end to ensure i < j.

Common Pitfalls & Bugs

Failing to update best_left after computing the score for j can cause missing better prefix candidates, leading to incorrect results.

Complexity

This single-pass approach achieves O(n) time and O(1) space, optimal for the problem constraints.

Line Analysis

This solution has 2 Critical lines interviewers watch for.

Line 7 Critical
best = max(best, best_left + values[j] - j)

Update the global maximum score by comparing current best with the candidate score best_left + values[j] - j.

This line embodies the core greedy step: combining the best prefix value with the current suffix value to find the maximum score efficiently in O(1) time per iteration.

Line 8 Critical
best_left = max(best_left, values[j] + j)

Update best_left to be the maximum of itself and values[j] + j for future iterations.

Updating best_left ensures that the prefix value always reflects the best possible values[i] + i for all i < j, which is critical for correctness and optimality.

Full line-by-line criticality + rationale for all 6 lines available on Pro.

Test Your Understanding

Why is it valid to maintain only the maximum values[i] + i seen so far when computing the score for each j?

See the answer with Pro.

Related Problems

Greedy Algorithms pattern

Don't just read it. Drill it.

Reconstruct Best Sightseeing Pair from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Best Sightseeing Pair drill

or drill a free problem