Can the first player win?
Two players take turns removing a number from either end of the list nums and adding it to their score. Both
play optimally. Return true if the first player's final score is at least the second player's.
Examples
| Input | Output | Why |
|---|---|---|
nums = [1,5,2] | false | Whichever end the first player takes, the second takes the 5. |
nums = [1,5,233,7] | true | Take 1; whatever comes next, the 233 is reachable. |
nums = [7] | true | Take it. |
Constraints
- 1 ≤ nums.length ≤ 20
- 0 ≤ nums[i] ≤ 10⁷
Hints
Hint 1
Track the score difference, not both scores: diff(i, j) = the most the player to move can lead by on nums[i..j].
Hint 2
Taking nums[i] leaves the opponent to move on [i + 1, j]: nums[i] − diff(i + 1, j). Similarly for nums[j].
Hint 3
The first player wins or ties when diff(0, n − 1) ≥ 0.
Where people lose marks
- Tracking both scores with greedy choices (taking the larger end is not optimal).
- Treating a tie as a loss: the problem counts it as a win.
Dry run on [1,5,233,7]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 4from 010·1·2·3from 1051·2·3from 2012332·3from 301273
Length 1: best lead for the player to move, [0, 0]: 1, [1, 1]: 5, [2, 2]: 233, [3, 3]: 7.
-
Step 2 / 4from 01041·2·3from 10512282·3from 20123322263from 301273
Length 2: best lead for the player to move, [0, 1]: 4, [1, 2]: 228, [2, 3]: 226.
-
Step 3 / 4from 010412292·3from 10512282-2213from 20123322263from 301273
Length 3: best lead for the player to move, [0, 2]: 229, [1, 3]: -221.
-
Step 4 / 4from 0104122922223from 10512282-2213from 20123322263from 301273
Length 4: best lead for the player to move, [0, 3]: 222. diff(0, 3) = 222: the first player wins or ties.
How to think about it
A two-player game becomes a single-valued DP by measuring the difference between the mover's score and the other
player's. On the interval [i, j], the player to move takes an end and then becomes the opponent on what remains, so
diff(i, j) = max(nums[i] − diff(i + 1, j), nums[j] − diff(i, j − 1)), with diff(i, i) = nums[i].
Fill by increasing length; the first player wins or ties when diff(0, n − 1) ≥ 0. This minimax-as-negation trick
is common in quant interviews.
Complexity. Time O(n²) · Space O(n)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def predict_the_winner(nums):
n = len(nums)
diff = nums[:] # diff[j] for the interval [i, j]; length 1: the number itself
for i in range(n - 2, -1, -1):
for j in range(i + 1, n):
# take the left end (opponent then leads by the old diff[j]) or the right end
diff[j] = max(nums[i] - diff[j], nums[j] - diff[j - 1])
return diff[n - 1] >= 0#include <algorithm>
#include <vector>
bool solve(const std::vector<int>& nums) {
const int n = nums.size();
std::vector<long long> diff(nums.begin(), nums.end());
for (int i = n - 2; i >= 0; --i)
for (int j = i + 1; j < n; ++j) diff[j] = std::max(nums[i] - diff[j], nums[j] - diff[j - 1]); // left or right end
return diff[n - 1] >= 0;
}class Solution {
public boolean predictTheWinner(int[] nums) {
int n = nums.length;
long[] diff = new long[n];
for (int i = 0; i < n; i++) diff[i] = nums[i];
for (int i = n - 2; i >= 0; i--)
for (int j = i + 1; j < n; j++) diff[j] = Math.max(nums[i] - diff[j], nums[j] - diff[j - 1]); // left or right end
return diff[n - 1] >= 0;
}
}function predictTheWinner(nums) {
const n = nums.length, diff = [...nums];
for (let i = n - 2; i >= 0; i--)
for (let j = i + 1; j < n; j++) diff[j] = Math.max(nums[i] - diff[j], nums[j] - diff[j - 1]); // left or right end
return diff[n - 1] >= 0;
}fn solve(nums: &[i64]) -> bool {
let n = nums.len();
let mut diff = nums.to_vec();
for i in (0..n.saturating_sub(1)).rev() {
for j in i + 1..n {
diff[j] = (nums[i] - diff[j]).max(nums[j] - diff[j - 1]); // left or right end
}
}
diff[n - 1] >= 0
}import Data.Array
solve :: [Int] -> Bool
solve nums = diff ! (0, n - 1) >= 0
where
n = length nums
a = listArray (0, n - 1) nums
-- diff ! (i, j): the most the player to move can lead by on nums[i..j]
diff = array ((0, 0), (n - 1, n - 1)) [((i, j), go i j) | i <- [0 .. n - 1], j <- [0 .. n - 1]] :: Array (Int, Int) Int
go i j
| i > j = 0
| i == j = a ! i
| otherwise = max (a ! i - diff ! (i + 1, j)) (a ! j - diff ! (i, j - 1))
Variants to try
- With an even number of piles, the first player can always secure the larger of the odd-indexed and even-indexed sums.
- Return the best final score of each player.
- Players may take from either end up to k numbers.
Next problems
Burst balloons for the most coins
Bursting a balloon earns the product of it and its current neighbours. Choosing which balloon goes last in each range makes the two sides independent.
Open the problem → Hard30 minThe cheapest way to cut a stick
Each cut costs the length of the piece being cut. Choose the order of cuts to minimise the total: an interval DP over the sorted cut positions.
Open the problem → Easy10 minThe cheapest way up a staircase
Each step has a cost; climb one or two steps at a time; start on step 0 or 1. Return the cheapest way past the top.
Open the problem →Write a solution and run it against the real test table.
Keep going
Burst balloons for the most coins
Bursting a balloon earns the product of it and its current neighbours. Choosing which balloon goes last in each range makes the two sides independent.
Open the problem → Hard30 minThe cheapest way to cut a stick
Each cut costs the length of the piece being cut. Choose the order of cuts to minimise the total: an interval DP over the sorted cut positions.
Open the problem → Easy10 minThe cheapest way up a staircase
Each step has a cost; climb one or two steps at a time; start on step 0 or 1. Return the cheapest way past the top.
Open the problem → Dynamic programmingThe pattern behind it
Write the recursion, notice the repeats, fill a table in an order that makes each entry final.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.