Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

375. Guess Number Higher or Lower II - Leetcode Solution

Code Implementation

class Solution:
    def getMoneyAmount(self, n: int) -> int:
        dp = [[0] * (n + 2) for _ in range(n + 2)]
        for length in range(2, n + 1):
            for start in range(1, n - length + 2):
                end = start + length - 1
                dp[start][end] = float('inf')
                for piv in range(start, end):
                    cost = piv + max(dp[start][piv - 1], dp[piv + 1][end])
                    dp[start][end] = min(dp[start][end], cost)
        return dp[1][n]
      
class Solution {
public:
    int getMoneyAmount(int n) {
        vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
        for (int len = 2; len <= n; ++len) {
            for (int start = 1; start <= n - len + 1; ++start) {
                int end = start + len - 1;
                dp[start][end] = INT_MAX;
                for (int piv = start; piv < end; ++piv) {
                    int cost = piv + max(dp[start][piv - 1], dp[piv + 1][end]);
                    dp[start][end] = min(dp[start][end], cost);
                }
            }
        }
        return dp[1][n];
    }
};
      
class Solution {
    public int getMoneyAmount(int n) {
        int[][] dp = new int[n + 2][n + 2];
        for (int len = 2; len <= n; ++len) {
            for (int start = 1; start <= n - len + 1; ++start) {
                int end = start + len - 1;
                dp[start][end] = Integer.MAX_VALUE;
                for (int piv = start; piv < end; ++piv) {
                    int cost = piv + Math.max(dp[start][piv - 1], dp[piv + 1][end]);
                    dp[start][end] = Math.min(dp[start][end], cost);
                }
            }
        }
        return dp[1][n];
    }
}
      
var getMoneyAmount = function(n) {
    const dp = Array.from({length: n + 2}, () => Array(n + 2).fill(0));
    for (let len = 2; len <= n; ++len) {
        for (let start = 1; start <= n - len + 1; ++start) {
            let end = start + len - 1;
            dp[start][end] = Infinity;
            for (let piv = start; piv < end; ++piv) {
                let cost = piv + Math.max(dp[start][piv - 1], dp[piv + 1][end]);
                dp[start][end] = Math.min(dp[start][end], cost);
            }
        }
    }
    return dp[1][n];
};
      

Problem Description

The "Guess Number Higher or Lower II" problem asks you to play a number guessing game with a twist: you must minimize the worst-case amount of money you could lose. Given an integer n, you must guess a number between 1 and n. Each time you guess a number x and it is incorrect, you pay x dollars. The game then continues with either the range 1 to x-1 or x+1 to n, depending on whether the target number is lower or higher. Your goal is to find the minimum amount of money required to guarantee a win, no matter what the target number is.

  • You can only guess numbers in the range 1 to n.
  • You must find a strategy that minimizes your maximum possible loss.
  • Each wrong guess costs you the value of the guessed number.
  • The answer should be the minimum money needed to guarantee a win for any target in the range.

Thought Process

At first glance, this problem seems similar to binary search, where you try to minimize the number of guesses. However, the twist is that each guess has a different "cost" (equal to the guessed number), and you need to minimize the maximum cost you could possibly pay, not the number of guesses. If you always guess the middle, you might not always minimize the worst-case cost, because the cost of a wrong guess can be high.

A brute-force approach would be to try every possible sequence of guesses for every possible range, but this quickly becomes infeasible as n grows. The key insight is to use dynamic programming: for each possible range of numbers, calculate the minimum cost required to guarantee a win, and build up solutions for larger ranges using solutions for smaller ranges. This way, we avoid redundant calculations and efficiently find the optimal strategy.

Solution Approach

We solve the problem using dynamic programming. Here's the step-by-step method:

  1. Define Subproblems:
    • Let dp[start][end] be the minimum cost to guarantee a win for the range [start, end].
  2. Base Cases:
    • If start >= end, then no guesses are needed, so dp[start][end] = 0.
  3. Recursive Relation:
    • For each possible first guess piv in [start, end], the cost is piv (the cost of guessing) plus the maximum cost of the two resulting subranges: dp[start][piv-1] and dp[piv+1][end].
    • We want to minimize the maximum cost across all guesses. So:
      • dp[start][end] = min_{piv in [start, end]} [piv + max(dp[start][piv-1], dp[piv+1][end])]
  4. Build Up the Table:
    • Start with small ranges and build up to larger ranges.
    • Fill the table dp[start][end] for all start and end where start < end.
  5. Return the Result:
    • The answer is dp[1][n], the minimum cost to guarantee a win in the full range.

We use a 2D array for memoization because we need to store the minimum cost for every possible subrange. By always considering the worst-case cost at each guess and minimizing over all possible first guesses, we ensure the strategy is optimal.

Example Walkthrough

Let's walk through the problem for n = 4:

  • Range [1,4]:
    • Guess 1: Cost is 1 + dp[2][4] = 1 + ?
    • Guess 2: Cost is 2 + max(dp[1][1], dp[3][4]) = 2 + max(0, ?)
    • Guess 3: Cost is 3 + max(dp[1][2], dp[4][4]) = 3 + max(?, 0)
    • Guess 4: Cost is 4 + dp[1][3] = 4 + ?
  • Compute smaller ranges first:
    • dp[3][4]: Guess 3 (cost 3 + dp[4][4]=3), Guess 4 (cost 4 + dp[3][3]=4); the minimum is 3.
    • dp[2][3]: Guess 2 (2 + dp[3][3]=2), Guess 3 (3 + dp[2][2]=3); minimum is 2.
    • dp[2][4]: Try guesses 2, 3, 4:
      • Guess 2: 2 + dp[3][4]=2+3=5
      • Guess 3: 3 + max(dp[2][2], dp[4][4])=3+max(0,0)=3
      • Guess 4: 4 + dp[2][3]=4+2=6
      Minimum is 3.
    • dp[1][2]: Guess 1 (1+dp[2][2]=1), Guess 2 (2+dp[1][1]=2); minimum is 1.
    • dp[1][3]: Try guesses 1, 2, 3:
      • Guess 1: 1+dp[2][3]=1+2=3
      • Guess 2: 2+max(dp[1][1],dp[3][3])=2+max(0,0)=2
      • Guess 3: 3+dp[1][2]=3+1=4
      Minimum is 2.
  • Now compute dp[1][4]:
    • Guess 1: 1 + dp[2][4] = 1 + 3 = 4
    • Guess 2: 2 + max(dp[1][1], dp[3][4]) = 2 + max(0,3) = 5
    • Guess 3: 3 + max(dp[1][2], dp[4][4]) = 3 + max(1,0) = 4
    • Guess 4: 4 + dp[1][3] = 4 + 2 = 6
    The minimum is 4.

So, for n = 4, the minimum amount of money needed to guarantee a win is 4.

Time and Space Complexity

  • Brute-force Approach:
    • Would try every possible guessing sequence for every range, leading to exponential time complexity: O(2^n) or worse.
  • Optimized Dynamic Programming Approach:
    • There are O(n^2) subproblems (one for each possible [start, end] range).
    • For each subproblem, we try up to O(n) possible pivots (guesses).
    • Total time complexity is O(n^3).
    • Space complexity is O(n^2) for the DP table.

The dynamic programming solution is efficient enough for the constraints given in the problem.

Summary

The "Guess Number Higher or Lower II" problem is a classic example of dynamic programming applied to minimax optimization. Instead of simply guessing the middle number, we carefully analyze each possible guess to minimize the worst-case cost, using a DP table to store subproblem solutions. This approach guarantees that, no matter what the target number is, we will never pay more than the computed minimum amount. The elegance of the solution lies in breaking the problem into manageable subproblems and always considering the worst-case scenario at each step.