375. Guess Number Higher or Lower II

本文介绍了一个猜数字游戏的最优策略,目标是最小化最坏情况下的支付总额。通过动态规划方法,采用区间型DP思想,从短区间推导长区间,最终求解出在给定范围内必胜所需的最小资金。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

We are playing the Guess Game. The game is as follows:

I pick a number from 1 to n. You have to guess which number I picked.

Every time you guess wrong, I'll tell you whether the number I picked is higher or lower.

However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.

Example:

n = 10, I pick 8.

First round:  You guess 5, I tell you that it's higher. You pay $5.
Second round: You guess 7, I tell you that it's higher. You pay $7.
Third round:  You guess 9, I tell you that it's lower. You pay $9.

Game over. 8 is the number I picked.

You end up paying $5 + $7 + $9 = $21.

Given a particular n ≥ 1, find out how much money you need to have to guarantee a win.


摘自

https://discuss.leetcode.com/topic/51358/java-dp-solution

https://discuss.leetcode.com/category/495/guess-number-higher-or-lower-ii


Definition of dp[i][j]: minimum number of money to guarantee win for subproblem [i, j].

Target: dp[1][n]

Corner case: dp[i][i] = 0 (because the only element must be correct)

Equation: we can choose k (i<=k<=j) as our guess, and pay price k. After our guess, the problem is divided into two subproblems. Notice we do not need to pay the money for both subproblems. We only need to pay the worst case (because the system will tell us which side we should go) to guarantee win. So dp[i][j] = min (i<=k<=j) { k + max(dp[i][k-1], dp[k+1][j]) }

public class Solution {
    public int getMoneyAmount(int n) {
        if (n == 1) {
            return 0;
        }
        int[][] dp = new int[n + 1][n + 1];
        for (int jminusi = 1; jminusi < n; jminusi++) {
            for (int i = 0; i + jminusi <= n; i++) {
                int j = i + jminusi;
                dp[i][j] = Integer.MAX_VALUE;
                for (int k = i; k <= j; k++) {
                    dp[i][j] = Math.min(dp[i][j],
                                        k + Math.max(k - 1 >= i ? dp[i][k - 1] : 0,
                                                     j >= k + 1 ? dp[k + 1][j] : 0));
                }
            }
        }
        return dp[1][n];
    }
}

---------------------------------------------------------------------------------------------------------------------

区间型DP先求出短的区间,由短的区间推出长的区间。

我们需要按照一定的顺序计算dp的值,由于计算dp[i][j]时我们需要计算dp[i][j-1],dp[i+1][j],所以我们按照j-i递增的顺序计算,即l区间长度len由短到长的顺序计算。


public static int getMoneyAmount(int n)
	{
        if (n == 1)
            return 0;
        
        int[][] dp = new int[n + 1][n + 1];
        
        for (int len = 1; len < n; len++)
            for (int i = 0; i + len <= n; i++)
            {
                int j = i + len;
                dp[i][j] = Integer.MAX_VALUE;
                for (int k = i; k <= j; k++) 
                    dp[i][j] = Math.min(dp[i][j],k + Math.max(k-1>=i?dp[i][k - 1]:0,k+1<=j?dp[k + 1][j]:0));
            }
        
        return dp[1][n];
    }


---------------------------------------------------------------------------------------------------

记忆优化搜索

public class Solution {
    public int getMoneyAmount(int n) {
        int[][] table = new int[n+1][n+1];
        return DP(table, 1, n);
    }
    
   public int DP(int[][] t, int s, int e){
        if(s >= e) return 0;
        if(t[s][e] != 0) return t[s][e];
        int res = Integer.MAX_VALUE;
        for(int x=s; x<=e; x++){
            int tmp = x + Math.max(DP(t, s, x-1), DP(t, x+1, e));
            res = Math.min(res, tmp);
        }
        t[s][e] = res;
        return res;
    }
}


在LeetCode的第374题“猜数字游戏”(Guess Number Higher or Lower)中,玩家和计算机进行一种简单的猜数字游戏。玩家需要猜测一个由计算机随机生成的1到n之间的整数,如果玩家猜的数字太小,计算机会提示“太低”,如果玩家猜的数字太大,计算机则提示“太高”。玩家有最多次数限制来猜测这个数字。 C++中可以使用回溯法(Backtracking)策略来解决这个问题。基本步骤如下: 1. 定义一个递归函数,接受当前猜测的数字、剩余的最大猜测次数以及已尝试过的数字范围。 2. 在函数开始时,设置一个标志变量,表示当前猜测的是最后一次机会。然后从剩余的数字范围内随机选择一个数字作为猜测。 3. 检查猜测的数字与实际数字的关系,如果是正确的,则返回胜利;如果太小,递归地调用函数并减去1次机会;如果太大,直接返回失败。 4. 在所有猜测机会用完之前,继续尝试新的数字,直到找到正确答案或者机会耗尽。 以下是简化的C++代码示例: ```cpp #include <vector> #include <ctime> int guessNumber(int n, vector<int>& guesses) { srand(time(0)); // 初始化随机种子 int target = rand() % (n + 1); // 生成1到n之间的随机数 int attempts = guesses.size(); // 最大尝试次数等于已给出的猜测次数 bool lastChance = false; for (int i = 0; i <= attempts && !lastChance; ++i) { int guess = guesses[i]; // 使用已知的猜测值 if (guess == target) { return i + 1; // 胜利,返回猜测次数 } else if (guess < target) { cout << "太低了 (" << guess << ")"; lastChance = i == attempts - 1; // 如果这是最后的猜测,标记为最后一次机会 } else { // guess > target cout << "太高了 (" << guess << ")"; lastChance = true; } } return -1; // 如果超过最大次数仍未能猜中,返回-1 } // 示例调用 int main() { vector<int> guesses = {2, 3, 5}; // 给出已有的猜测值 cout << guessNumber(10, guesses); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值