问题描述
在一个数字游戏中,数字 1 到 n 中有一个是幸运数字,且这个数字是等概率随机选择的。玩家 小I 和 小W 轮流从这些数字中猜测幸运数字,小I 总是先开始猜。
游戏规则如下:每轮游戏后,主持人会宣布猜测结果是太大了、太小了,还是正确的。如果猜中,那么该轮猜测者即为游戏的获胜者。
假设 小I 和 小W 都采取最优策略,并且都知道对方也是如此,本题要求计算 小I 获胜的概率。请输出保留五位小数的结果。
解题思路
先说结论,这个题只需要一个式子就能做出来,Pn=⌈2n⌉/n(⌈ ⌉是向上取整),下面我来分析一下。
首先,给定一个 n 值, 小I 获胜的概率为 Pn 。 小I 从这 n 个数字中任取一个 i ,他会有 n1 的概率立即获胜,否则,数字就被划分为了两部分,一部分是 1~(i-1) 共 i-1 个数字,另一部分是 (i+1)~n 共 n-i 个数字。幸运数字有 ni−1 的概率落在前一个部分,有 nn−i 的概率落在后一个部分。
如果幸运数字落在前一个部分,就相当于从 小W 开始在 i-1 个数字中猜, 小I 获胜的概率就是 1−Pi−1 ,另一种情况同理。并且两人都会采取最优策略,所以最终可以得到Pn=max1≤i≤n(n1+ni−1∗(1−Pi−1)+nn−i∗(1−Pn−i)) 。
接下来,我们要考虑怎么能让 Pn 的值最大。我首先手动计算出一些 P 的最大值 P1=11 , P2=21 , P3=32 , P4=42 , P5=53 , P6=63 , P7=74 , P8=84(不约分可以更好地看出规律)。这里其实就已经能看出来结论了,但是还没有严谨的证明,下一步就是要证明这个结论。
数学证明
首先,可以通过很简单的数学归纳得出每一个 Pn 的分母就是n(不约分的情况下),为了简化计算,设 Pn 的分子为 an ,可以得出 Pn=max1≤i≤nn1+((i−1)−ai−1)+((n−i)−an−i)=max1≤i≤nnn−(ai−1+an−i) ,即an=max1≤i≤n(n−(ai−1+an−i)) 。
然后我们使用数学归纳法证明 an=⌈2n⌉ ,已经证明在 n≤8 时命题成立。假设在 n≤m 时命题成立,那么在 n=m+1 时,为使 am+1=m+1−(⌈2i−1⌉+⌈2m+1−i⌉) 取最大值,我们进行分类讨论:若 m + 1 为奇数,令 i 为奇数则 am+1=2m+2 ,令 i 为偶数则 am+1=2m ;若 m + 1 为偶数,令 i 为奇数则 am+1=2m+1 ,令 i 为偶数则 am+1=2m+1 。可以看出,无论 m + 1 为奇数还是偶数,只要令 i 为奇数就可以使 am+1 取得最大值,且满足am+1=⌈2m+1⌉ 。
由此得证,Pn=⌈2n⌉/n 。
代码实现
得到上面的结论,代码就可以写得很简短了。
std::string solution(int n) {
if(n == 1) return "1.00000";
int result = ((n + 1) / 2 * 1000000 / n + 5) / 10;
return "0." + std::to_string(result % 100000);
}
除了 n=1 时概率为 1 ,其余情况下概率都是小于 1 的。用整型来计算小数点后数字, (n + 1) / 2 是计算 ⌈2n⌉ , (x * 1000000 / n + 5) / 10 是四舍五入保留五位。
复杂度分析
数学推理复杂度 ↑↑↑
代码复杂度O(1)