BZOJ 2318: Spoj4060 game with probability Problem (概率dp)(博弈论)

2318: Spoj4060 game with probability Problem

Description

AliceBob在玩一个游戏。有n个石子在这里,AliceBob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,同样,Bobq的概率投掷出他相投的一面。

现在Alice先手投掷硬币,假设他们都想赢得游戏,问你Alice胜利的概率为多少。

Input

第一行一个正整数t,表示数据组数。

对于每组数据,一行三个数npq

Output

对于每组数据输出一行一个实数,表示Alice胜利的概率,保留6位小数。

Sample Input

1

1 0.5 0.5

Sample Output

0.666667

HINT

数据范围:

1<=t<=50

0.5<=p,q<=0.99999999

对于100%的数据 1<=n<=99999999


传送门

 

概率dp的部分不难写,博弈论的策略没想出来。

设$f[i]$表示有$i$枚石子,先手获胜的概率;

   $g[i]$表示有$i$枚石子,后手获胜的概率;

则当有$i+1$枚石子时:

  若$f[i]>g[i]$,则$Alice$希望在有$i$枚石子时取得先手,那么她希望这轮不取;

  否则,$Alice$希望这轮取得石子;

当Alice希望取得石子时:

  若Alice在本轮中先手,则有$p$的概率在下轮取得后手,有$1-p$的概率在本轮取得后手;

  若Alice在本轮中后手,则有$q$的概率在下轮取得先手,有$1-q$的概率在本轮取得先手;

即$$\begin{cases}f_i=p*g_{i-1}+(1-p)*g_i\\g_i=q*f_{i-1}+(1-q)*f_i\end{cases}$$

化简得$$f_i=\frac{p*g_{i-1}+(1-p)*q*f_{i-1}}{1-(1-p)*(1-q)}$$

$$g_i=\frac{q*f_{i-1}+(1-q)*p*g_{i-1}}{1-(1-p)*(1-q)}$$


当Alice不希望取得石子时:$p$和$(1-p)$ $q$和$(1-q)$取反即可

 

倒推dp即可。

有两个注意的点:

  n很大,数组肯定是装不下的,显然要用滚动数组。

  O(n)的时间复杂度显然是跑不过的,打表可以发现答案是收敛的,n很大的时候前6位小数已经固定了,n取min(n,100)就好。似乎是个常见的套路,还是too young,见得不够多。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define foru(i,x,y) for(int i=x;i<=y;i++)
 4 using namespace std;
 5 int T,n,fl;
 6 double f[2],g[2],p,q,p_,q_;
 7 int main(){
 8     scanf("%d",&T);
 9     while(T--){
10         scanf("%d%lf%lf",&n,&p,&q);
11         n=min(n,100);
12         f[0]=0;g[0]=1;q_=1-q;p_=1-p;
13         int i=1;
14         foru(j,1,n){
15             if(f[i^1]>g[i^1])swap(p_,p),swap(q_,q),fl=1;
16                 f[i]=(p*g[i^1]+p_*q*f[i^1])/(1-q_*p_);
17                 g[i]=(q*f[i^1]+q_*p*g[i^1])/(1-q_*p_);
18             if(fl==1)swap(p_,p),swap(q_,q),fl=0;i^=1;
19         }
20         printf("%.6lf\n",f[i^1]);
21     }
22 }

 

posted @ 2017-06-15 13:42  羊毛羊  阅读(453)  评论(0编辑  收藏  举报