Bzoj 2318 Spoj4060 game with probability Problem
2318: Spoj4060 game with probability Problem
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 524 Solved: 262
[Submit][Status][Discuss]
Description
Alice和Bob在玩一个游戏。有n个石子在这里,Alice和Bob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,同样,Bob有q的概率投掷出他相投的一面。
现在Alice先手投掷硬币,假设他们都想赢得游戏,问你Alice胜利的概率为多少。
Input
第一行一个正整数t,表示数据组数。
对于每组数据,一行三个数n,p,q。
Output
对于每组数据输出一行一个实数,表示Alice胜利的概率,保留6位小数。
Sample Input
1 0.5 0.5
Sample Output
HINT
数据范围:
1<=t<=50
0.5<=p,q<=0.99999999
对于100%的数据 1<=n<=99999999
一道无比有趣的概率题……
我们设f[i]为当前有i张牌且我们当前所针对人物为先手的获胜概率,g[i]为后手获胜概率。
那么,我们先考虑一下选择希望翻正面的概率的递推式。
f[i]=p*g[i-1]+(1-p)*g[i]
因为我们有p的几率将当前局面改为后手、场上有i-1张牌,1-p的几率将局面改为g[i]所以我们上述递推式正确的。g[i]也是如此。
g[i]=q*f[i-1]+(1-q)*f[i]
为什么这里变为q呢?因为当前我们希望翻正面一定是因为g[i-1]>f[i-1],而这个概率对于谁都是一样的,所以bob也一定是想要去翻正面,所以改为q。
虽然这样我们好像还是无法推出来啊。所以我们要去解一下方程组,反正不就是一个二元一次吗?手解一下不就好了,应该不用高斯消元吧……
至于希望翻反面,请读者自己思考,答案就在代码里(当然不是原始状态啦)。
有意思的是虽然他的n很大,但是他并未对精度要求太高,所以我们只要等到他的精度卡到一定地步直接跳出就好了。
我一开始是直接判断精度,但是貌似T了,所以看别人算到1000就行了……
1 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #include <cmath> 8 #include <queue> 9 #include <map> 10 #include <set> 11 #include <vector> 12 #define N 1005 13 using namespace std; 14 int t,n; 15 double p,q,f[N],g[N]; 16 int main() 17 { 18 scanf("%d",&t); 19 while(t--) 20 { 21 scanf("%d%lf%lf",&n,&p,&q); 22 memset(f,0,sizeof(f)); 23 memset(g,0,sizeof(g)); 24 g[0]=1; 25 n=min(n,1000); 26 for(int i=1;i<=n;i++) 27 { 28 if(f[i-1]<g[i-1]) 29 { 30 f[i]=(p*g[i-1]+(1.0-p)*q*f[i-1])/(p+q-p*q); 31 g[i]=(q*f[i-1]+(1.0-q)*p*g[i-1])/(p+q-p*q); 32 } 33 else 34 { 35 f[i]=(p*(1.0-q)*f[i-1]+(1.0-p)*g[i-1])/(1.0-p*q); 36 g[i]=((1.0-q)*f[i-1]+q*(1.0-p)*g[i-1])/(1.0-p*q); 37 } 38 39 } 40 printf("%.6lf\n",f[n]); 41 } 42 return 0; 43 }