Dynamic Programming · Hard

330. Optimal Strategy for a Game

Topics: Dynamic Programming, Game Theory

Solve it on DeVert Campus - free code runner

Problem

n coins with given values are arranged in a row. Two players alternately pick a coin from either end of the remaining row (the first player moves first), each keeping the value of every coin they pick, and both play optimally to maximize their own total. Find the maximum total value the first player can guarantee for themself.

Input and output

Input: The first line contains an integer n. The second line contains n space-separated integers, the coin values in a row. Output: Print a single integer - the maximum total value the first player can guarantee.

Example

Input:
4
8 15 3 7
Output:
22

Constraints

1 <= n <= 1000

Key observation

This is a classic example of MINIMAX-style reasoning applied via DP - recognizing that 'both players play optimally' means modeling the OPPONENT's optimal (adversarial, from your perspective) choice explicitly within your own recurrence, not just computing your own best-case scenario.

Intuition

Two players alternately pick coins from either end of a row, both playing optimally - find the maximum the first player can guarantee. Brute force: Try every possible sequence of picks for both players directly - this branches combinatorially (2^n possible pick sequences), impractical without memoization. Optimised: The KEY insight is that after YOU pick an end, your OPPONENT plays OPTIMALLY too - meaning they'll leave YOU the WORSE of your two possible next choices, not the better one, since they're trying to maximize THEIR OWN total, not help you. This adversarial 'assume the opponent plays against you optimally' framing is what the min() inside the recurrence captures precisely.

Brute Force - Recursive Minimax

Recursively compute, for the current remaining range of coins [i, j], the best total the current player can guarantee by trying both choices - take the leftmost coin and then let the opponent play optimally on [i+1, j], or take the rightmost coin and let the opponent play optimally on [i, j-1] - and picking whichever choice leaves the current player with more after accounting for what the opponent will optimally leave them with. This directly models the alternating optimal-play structure of the game, but without caching, the same [i, j] ranges get recomputed repeatedly across different recursive paths, causing exponential blowup. Time: O(2^n) Space: O(n)

Better - Top-Down Memoized Recursion

Use the same recursive minimax logic as the brute force, but cache the result for every distinct range [i, j] the first time it is computed, so subsequent calls for the same range return instantly. This is correct because the optimal value for any range only ever depends on the two smaller sub-ranges obtained by removing an end, and there are only O(n^2) distinct ranges total, so memoizing collapses the exponential recursion tree down to one computation per range. Time: O(n^2) Space: O(n^2)

Optimal - Bottom-Up DP Over Diagonals With Reduced Space

Fill the DP table iteratively by increasing subarray length rather than recursively: for length 1 ranges the answer is just the single coin's value, and for each larger length, dp[i][j] = max(coin[i] + min(dp[i+2][j], dp[i+1][j-1]), coin[j] + min(dp[i][j-2], dp[i+1][j-1])) (treating out-of-range sub-problems as 0). Since each length's row only ever depends on the two previous shorter-length rows, only those are kept in memory instead of the full 2D table. This computes exactly the same values as the memoized version but processes them in a controlled order that allows discarding older diagonals, cutting memory from quadratic down to linear. Time: O(n^2) Space: O(n)

Step by step

1. dp[i][j] = the maximum value the player TO MOVE can guarantee from subarray arr[i..j], assuming both play optimally from here on. 2. Picking arr[i] leaves the OPPONENT facing arr[i+1..j] - whichever end they then pick, they leave YOU the WORSE of the two remaining sub-ranges for your next turn, so picking i is worth arr[i] + min(dp[i+2][j], dp[i+1][j-1]). 3. Picking arr[j] is symmetric; dp[i][j] = the MAX of these two picks, since the CURRENT player chooses whichever option is better for THEM.

Complexity

Time: O(n²) - filling the interval DP table, one cell per (i,j) range. Space: O(n²), for the full DP table.

Edge cases

Test n = 1 (the first player simply takes the only coin) and n at its upper bound of 1000 for the O(n^2) DP.

Interview tip

Be ready to explain the min()-inside-max() structure clearly - it's easy to mistakenly think 'my next turn gets the better sub-range', but a truly optimal opponent will always leave you the WORSE one, which is exactly why min() appears nested inside the max() choice.

All DSA problems