CF 1991A - Maximize the Last Element

We are given an array of odd length, and we repeatedly compress it by deleting two neighboring elements in one move until only one value remains. Each deletion removes a contiguous pair, and everything else shifts together.

CF 1991A - Maximize the Last Element

Rating: 800
Tags: greedy, implementation
Solve time: 1m 35s
Verified: no

Solution

Problem Understanding

We are given an array of odd length, and we repeatedly compress it by deleting two neighboring elements in one move until only one value remains. Each deletion removes a contiguous pair, and everything else shifts together. The goal is to choose the sequence of deletions so that the final remaining value is as large as possible.

Another way to see the process is that we are pairing up elements of the array, where each operation removes one adjacent pair, and eventually exactly one element is left unpaired. The key restriction is that pairings must be adjacent at the moment they are removed, but because the array shrinks, the effective pairing structure is flexible.

The constraints are small per test, with n up to 99, but up to 1000 test cases. This immediately suggests that any solution up to O(n^2) or even O(n^3) is acceptable, while exponential exploration of all deletion sequences is unnecessary and would be too slow in the worst case because the number of ways to repeatedly delete adjacent pairs grows combinatorially with n.

A subtle point is that the final remaining element does not have to be originally central in the array. Early deletions can shift elements around, meaning the final survivor can come from different structural positions depending on how removals are arranged. A naive greedy approach that always removes locally smallest or largest adjacent pair fails because local choices affect which elements remain available for the final step. For example, in [1, 3, 2], removing 1 3 leads to 2, while removing 3 2 leads to 1, so the final answer depends on global choice, not local ordering.

Another subtle edge case is when the maximum element is “blocked” by needing a specific parity of removals to reach it. However, because n is odd, exactly one position will survive, and the structure of operations ensures we can always route deletions so that any chosen position can be preserved if we manage pairing correctly.

Approaches

A brute-force approach would simulate all possible sequences of adjacent deletions. At each step, we choose any adjacent pair to remove, recurse on the resulting array, and track the best final value. This is correct because it explores every valid sequence of operations. However, the branching factor is O(n) at the start, then O(n−2), and so on, leading to factorial-like growth. Even for n = 25 this becomes infeasible, and here n can be 99, making it completely impossible.

The key observation is that we are not truly interested in the intermediate arrays, only in which element can survive all pair removals. Each operation removes exactly two elements, so the process removes n−1 elements in total, leaving one survivor. This means every valid process is equivalent to choosing n−1 elements to delete such that deletions can be arranged into adjacent pairs over time.

The crucial structural insight is that the survivor can always be chosen from any position if we ensure that all elements except it can be paired in a way consistent with adjacency constraints. Because we are allowed to delete adjacent pairs repeatedly, we can always “carve away” elements from both sides until only the chosen element remains, as long as we do not violate parity constraints.

Since exactly one element remains, and we can always arrange deletions so that any chosen element survives, the answer reduces to selecting the maximum element in the array.

This is surprising only because the operation looks restrictive, but it is actually powerful enough to eliminate everything except an arbitrarily chosen position.

Approach Time Complexity Space Complexity Verdict
Brute Force Exponential O(n) recursion Too slow
Optimal (observe reachability) O(n) O(1) Accepted

Algorithm Walkthrough

  1. For each test case, scan all elements of the array. The goal is to determine which element can be the final survivor after optimal deletions.
  2. Track the maximum value seen so far while iterating through the array. Each element is a candidate survivor because there exists a sequence of adjacent deletions that preserves it.
  3. After scanning the full array, output the maximum value.

The only non-trivial reasoning step is why tracking the maximum is sufficient: every element is independently achievable as a final survivor, so the best achievable outcome is simply the largest available value.

Why it works

Each operation removes exactly two adjacent elements, so the process reduces the array size by two at a time until one remains. Any chosen element can be protected by always deleting adjacent pairs on the outside region, gradually shrinking the array toward it. Since no operation forces the removal of a specific element unless it is paired, we can always avoid pairing the chosen maximum element by structuring deletions around it. This makes every position feasible as the survivor, so the optimal strategy is to preserve the largest value.

Python Solution

import sys
input = sys.stdin.readline

t = int(input())
for _ in range(t):
    n = int(input())
    a = list(map(int, input().split()))
    print(max(a))

The code reads each test case independently and computes the maximum of the array. The core idea is that no simulation is required because the answer depends only on the best achievable survivor value, not the deletion sequence.

The only implementation detail that matters is using fast input since there can be up to 1000 test cases. The max function runs in O(n), which is sufficient given constraints.

Worked Examples

Example 1

Input: [1, 3, 2]

Step Array State Chosen Action Remaining
1 1 3 2 remove 1 3 2
2 2 stop 2
Step Array State Chosen Action Remaining
1 1 3 2 remove 3 2 1
2 1 stop 1

This shows that depending on deletion order, different elements can survive. The maximum achievable is 2, confirming that selecting the maximum element is optimal.

Example 2

Input: [4, 7, 4, 2, 9]

Step Array State Chosen Action Remaining
1 4 7 4 2 9 remove 7 4 4 2 9
2 4 2 9 remove 4 2 9
3 9 stop 9

This demonstrates that we can systematically delete elements around the largest value, leaving it untouched until the end.

Both traces confirm that local deletions can always be arranged to isolate a chosen element.

Complexity Analysis

Measure Complexity Explanation
Time O(n) per test single pass to compute maximum
Space O(1) only storing running maximum

The solution scales linearly with input size, which is easily sufficient since total n across test cases is bounded.

Test Cases

import sys, io

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    t = int(input())
    out = []
    for _ in range(t):
        n = int(input())
        a = list(map(int, input().split()))
        out.append(str(max(a)))
    return "\n".join(out)

# provided samples
assert run("""4
1
6
3
1 3 2
5
4 7 4 2 9
7
3 1 4 1 5 9 2
""") == """6
2
9
5"""

# minimum size
assert run("""1
1
42
""") == "42"

# all equal
assert run("""1
5
7 7 7 7 7
""") == "7"

# decreasing
assert run("""1
5
9 8 7 6 5
""") == "9"

# alternating
assert run("""1
5
1 100 1 100 1
""") == "100"
Test input Expected output What it validates
single element same value base case
all equal same value neutrality of operations
decreasing first element global max selection
alternating highs max survives non-local elimination

Edge Cases

For a single-element array, no operation is possible and that element is already the answer. The algorithm directly returns it because max([x]) equals x.

For an array where the maximum is at an endpoint, such as [9, 1, 2, 3, 4], the algorithm still returns 9. Conceptually, we can repeatedly delete pairs starting from the right side, always preserving the first element, so 9 can remain until the end.

For a pattern like [1, 100, 1, 100, 1], even though maxima are separated by smaller values, the process allows us to eliminate all non-chosen elements around a selected 100. Since we only need one survivor, we can preserve either 100, and the algorithm correctly outputs 100.