UVa 11843 Guessing Game

题目描述

Alice\texttt{Alice}AliceBob\texttt{Bob}Bob 设计了一个双人猜数游戏。游戏开始前,他们约定两个正整数:范围 NNN允许的失误次数上限 SSSAlice\texttt{Alice}Alice 秘密选择一个整数 XXX0≤X<N0 \le X < N0X<N),然后 Bob\texttt{Bob}Bob 轮流猜测整数。对于 Bob\texttt{Bob}Bob 的每次猜测,Alice\texttt{Alice}Alice 会给出以下三种回应之一:

  • strike\texttt{strike}strike:如果猜测的数字大于 XXX
  • smile\texttt{smile}smile:如果猜测的数字小于 XXX
  • stop\texttt{stop}stop:如果猜测的数字等于 XXX(此时游戏结束,Bob\texttt{Bob}Bob 获胜)。

如果 Bob\texttt{Bob}Bob 累计收到 SSSstrike\texttt{strike}strike,则游戏结束,Alice\texttt{Alice}Alice 获胜。

Bob\texttt{Bob}Bob 希望在正式比赛前进行训练。他需要知道,对于给定的 NNNSSS在最坏情况下,他需要多少次猜测才能确保猜中 Alice\texttt{Alice}Alice 选择的任意数字 XXX,并且在此过程中他最多收到 S−1S-1S1strike\texttt{strike}strike(否则他会输掉游戏)。

输入格式:第一行包含一个整数 CCC,表示测试用例的数量。接下来 CCC 行,每行包含两个整数 NNNSSS,分别表示数字范围和允许的 strike\texttt{strike}strike 次数上限。保证 1≤N≤10001 \le N \le 10001N10001≤S≤201 \le S \le 201S20

输出格式:对于每个测试用例,输出一行一个整数,表示 Bob\texttt{Bob}Bob 在最坏情况下所需的最少猜测次数。

题目分析

这是一个典型的最坏情况下的最优策略问题,类似于带有限制的二分搜索。我们可以从以下几个关键点理解题目:

  1. 游戏目标Bob\texttt{Bob}Bob 需要保证无论 Alice\texttt{Alice}Alice 选择哪个数字 XXX,他都能在收到少于 SSSstrike\texttt{strike}strike 的情况下猜中。
  2. 策略限制:每次猜测如果得到 strike\texttt{strike}strike,会消耗一次“允许的失误机会”;如果得到 smile\texttt{smile}smile,则不消耗。
  3. 最坏情况:我们需要考虑在所有可能的 XXX 下,Bob\texttt{Bob}Bob 所需猜测次数的最大值,并最小化这个最大值。

解题思路

1. 问题建模

dp[n][s]dp[n][s]dp[n][s] 表示当数字范围为 [0,n−1][0, n-1][0,n1](即共有 nnn 个可能的数字),且 Bob\texttt{Bob}Bob 最多还能承受 sssstrike\texttt{strike}strike 时,在最坏情况下需要的最少猜测次数。

初始条件:
  • 如果没有数字需要猜(n=0n = 0n=0),则不需要猜测:dp[0][s]=0dp[0][s] = 0dp[0][s]=0
  • 如果不能承受任何 strike\texttt{strike}strikes=0s = 0s=0),那么 Bob\texttt{Bob}Bob 只能从最小的数字开始逐个猜测,最坏情况需要猜 nnn 次(当 X=n−1X = n-1X=n1 时):dp[n][0]=ndp[n][0] = ndp[n][0]=n
状态转移:

假设当前范围大小为 nnn,我们选择猜测位置 kkk0≤k<n0 \le k < n0k<n)。猜测后有三种可能:

  1. 猜中:游戏结束,本次猜测计数为 111
  2. 得到 strike\texttt{strike}strike(猜大了):说明 X<kX < kX<k,剩余范围大小为 kkk,剩余可承受 strike\texttt{strike}strike 次数为 s−1s-1s1,后续需要的最少猜测次数为 dp[k][s−1]dp[k][s-1]dp[k][s1]
  3. 得到 smile\texttt{smile}smile(猜小了):说明 X>kX > kX>k,剩余范围大小为 n−k−1n-k-1nk1,剩余可承受 strike\texttt{strike}strike 次数仍为 sss,后续需要的最少猜测次数为 dp[n−k−1][s]dp[n-k-1][s]dp[nk1][s]

由于我们要保证最坏情况,所以对于固定的 kkk,需要的猜测次数为:
guesses(k)=1+max⁡(dp[k][s−1], dp[n−k−1][s]) \texttt{guesses}(k) = 1 + \max(dp[k][s-1],\ dp[n-k-1][s]) guesses(k)=1+max(dp[k][s1], dp[nk1][s])
其中 111 表示当前这次猜测。

为了最小化最坏情况,我们选择最优的 kkk
dp[n][s]=min⁡k=0n−1(1+max⁡(dp[k][s−1], dp[n−k−1][s])) dp[n][s] = \min_{k=0}^{n-1} \left(1 + \max(dp[k][s-1],\ dp[n-k-1][s])\right) dp[n][s]=k=0minn1(1+max(dp[k][s1], dp[nk1][s]))

2. 算法实现

由于 N≤1000N \le 1000N1000S≤20S \le 20S20,我们可以使用动态规划预先计算所有可能的 dp[n][s]dp[n][s]dp[n][s] 值。对于每个测试用例,直接查表输出结果即可。

  • 时间复杂度O(N2⋅S)O(N^2 \cdot S)O(N2S),对于给定范围是可接受的。
  • 空间复杂度O(N⋅S)O(N \cdot S)O(NS)

3. 注意事项

  • 题目中给出的 SSSAlice\texttt{Alice}Alice 获胜所需的 strike\texttt{strike}strike 次数,因此 Bob\texttt{Bob}Bob 最多能承受 S−1S-1S1strike\texttt{strike}strike。我们在计算时实际使用的 s=S−1s = S-1s=S1
  • S=1S = 1S=1 时,Bob\texttt{Bob}Bob 不能承受任何 strike\texttt{strike}strikes=0s=0s=0),此时只能顺序猜测,需要 NNN 次。

代码实现

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 1005;
const int MAX_S = 25;
const int INF = 0x3f3f3f3f;

int dp[MAX_N][MAX_S];

// 预处理所有 dp[n][s] 的值
void precompute() {
    // 初始化:没有数字需要猜的情况
    for (int s = 0; s < MAX_S; ++s) dp[0][s] = 0;
    // 初始化:不能承受任何 strike 的情况
    for (int n = 1; n < MAX_N; ++n) dp[n][0] = n;
    
    // 动态规划递推
    for (int n = 1; n < MAX_N; ++n) {
        for (int s = 1; s < MAX_S; ++s) {
            int best = INF;
            // 尝试所有可能的猜测位置 k
            for (int k = 0; k < n; ++k) {
                int strikePart = dp[k][s - 1];      // 猜大后的情况
                int smilePart = dp[n - k - 1][s];   // 猜小后的情况
                int worst = 1 + max(strikePart, smilePart);
                if (worst < best) best = worst;
            }
            dp[n][s] = best;
        }
    }
}

int main() {
    precompute();  // 预先计算
    int c, n, S;
    cin >> c;
    while (c--) {
        cin >> n >> S;
        int s = S - 1;  // Bob 最多能承受的 strike 次数
        if (s < 0) s = 0;  // 安全处理边界
        cout << dp[n][s] << endl;
    }
    return 0;
}

样例解析

样例输入

2
3 2
4 1

样例输出

2
4
解释:
  1. N=3, S=2N=3,\ S=2N=3, S=2Bob\texttt{Bob}Bob 最多能承受 111strike\texttt{strike}strike。最优策略是先猜 111

    • 若得 strike\texttt{strike}strike,则 X=0X=0X=0,下一轮猜 000,共 222 次。
    • 若得 smile\texttt{smile}smile,则 X=2X=2X=2,下一轮猜 222,共 222 次。
    • 若猜中,则只需 111 次。
      最坏情况为 222 次。
  2. N=4, S=1N=4,\ S=1N=4, S=1Bob\texttt{Bob}Bob 不能承受任何 strike\texttt{strike}strikes=0s=0s=0)。只能从 000 开始顺序猜测,最坏情况(X=3X=3X=3)需要猜 0,1,2,30,1,2,30,1,2,3444 次。

总结

本题通过动态规划巧妙地处理了带有 strike\texttt{strike}strike 限制的猜数策略问题。关键在于定义状态 dp[n][s]dp[n][s]dp[n][s] 并找到正确的状态转移方程,即每次猜测后根据反馈(strike\texttt{strike}strikesmile\texttt{smile}smile)进入不同的子问题。预处理所有状态后,即可快速回答每个测试用例。该算法在给定数据范围内效率良好,是解决此类“最坏情况最优策略”问题的典型方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值