原帖:http://hi.baidu.com/atyuwen/blog/item/160bd024531e3034c995591d.html
Project Euler上最近的题目都还比较意思,来看看前些天刚刚新鲜出炉的一道题:Problem232,大意如此:说,有这样一个硬币游戏,需要两个玩家参与,我们不防分别将他们称为玩家1和玩家2。游戏规则如下:两个玩家轮流来掷硬币。玩家1每次只能掷一次,若是正面向上,则得1分,否则不得分。玩家2每次可以选择抛掷硬币的次数T(T>0),若每次都是正面向上,则得分为2的(T-1)次方,否则不得分。首先由玩家1先掷,先得分超过100者为赢家。现在问:假设玩家2每次都选择最优的投掷次数,则他赢的概率是多少?当然,每次的投掷都是完全公正和随机的,否则要来个刘谦,这概率就没法算了。
首先,可以凭直觉猜猜这个概率大概是多少。毫无疑问,大于1/2是肯定的,因为玩家2至少可以选择每次都只掷一次,在得分目标(100分)相对较大的时候,后掷的劣势可以忽略。嗯,好像没有其它线索了,不太好猜啊,还是老老实实算吧。
像这种算概率的题通常都比较麻烦,虽然很容易想到思路,但很难得到准确的答案。比如在这里,一个很容易想到的思路是动态规则。设P ( i, j )是当玩家1得到i分,玩家2得到j分,并且此时轮到玩家2抛掷时玩家2获胜的最大概率。从后往前推,先看P(99, 99)。此时,玩家2选择的抛掷次数肯定为1,得一分就够了,多了也浪费,还降低了得分的概率。当玩家2抛出硬币后,得到两种结果:得1分,不得分。得1分就直接赢了,而不得分就把主动权交给了玩家1,而当玩家1再次抛出后,又得到两种结果:得1分,则玩家1赢得游戏,不得分,则整个局势又回到P(99,99)的初始状态。上述过程有表达式如下:
P(99,99)= 1/2 + (1/2 * 1/2)* P(99,99)
解上述方程,可得P(99, 99)得2/3。注意到,上述表达式是一个边界情况,因为两个玩家都是再得一分即可获得胜利,现在推导一个更一般的状态转移方程。设在该轮玩家2选择的投掷次数为n, 这样可以得的分数s为2^(n-1),得分概率o 为1/2^(n+1)。则有:
P'(i, j)= o*(1/2)*P(i, j+s) + o*(1/2)*P(i+1, j+s) + (1-o)*(1/2)*P(i+1,j) + (1-o)*(1/2)*P'(i,j)
注意到上边的式子两边都有 P'( i, j ),所以是个隐式表达式。(昨天写漏了,加上一句)枚举所有使得(s+j)小于或者刚好大于100的n,计算上式中的P'(i, j),则有P(i, j)为这些P'(i, j)的最大值。当然,玩家2也可以选择令(s+j)远远大于100的投掷次数n, 但比较显然的是这样得到的P’(i, j)不会是最大的,比如前面所说的,在P(99, 99)的局势中玩家2明显不会选择超过1的投掷次数。有的地方只所以乘了二分之一是因为状态转移还需要玩家1的过渡,因为我们的状态定义是在当轮到玩家2抛掷时玩家2获胜的最大概率。为了表达方便,没有考虑一些边界的情况。然后按照上面的转移方程编写程序就行了,这道题的答案为:0.83648556.
这个答案是不是比用直觉猜的要高些呢。(我是猜的大概在2/3左右,误差不小),这首题其实非常适合用来做面试题:生动有趣,即不是太简单,也不是太难(比起以其相似的poj1074算是小乌见大乌了),虽然比较容易想到算法,却也不容易一次性算对。爷爷的,要是本人以后有荣幸当上面试官,一定要出出这道题。