题目描述 Description
Alice和Bob在玩一个游戏。有n个石子在这里,Alice和Bob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,同样,Bob有q的概率投掷出他相投的一面。
输入描述 Input Description
第一行一个正整数t,表示数据组数。
对于每组数据,一行三个数n,p,q。
输出描述 Output Description
对于每组数据输出一行一个实数,表示Alice胜利的概率,保留6位小数。
样例输入 Sample Input
1
1 0.5 0.5
样例输出 Sample Output
0.666667
数据范围及提示 Data Size & Hint
1<=t<=50,0.5<=p,q<=0.99999999,1<=n<=999999991<=t<=50,0.5<=p,q<=0.99999999,1<=n<=99999999
Solution
设f[i]f[i]为还剩ii个石子时轮到Alice,Alice的胜率,为还剩ii个石子时轮到Bob,Alice的胜率
假设每一次都是拿走石头最优,则会有
g[i]=q∗f[i−1]+(1−q)∗f[i]g[i]=q∗f[i−1]+(1−q)∗f[i]
将两式化为可以递推的形式,使f[i]f[i]从f[i−1]f[i−1]和g[i−1]g[i−1]转移,g[i]g[i]同理
最终化为:
好的,但是有个问题,就是并不一定每次都是拿走石头最优,万一这次拿走石头后对方马上就赢了呢?
所以说有时候可能会选择不拿走石子
不拿走石子时则会有:
f[i]=p∗g[i]+(1−p)∗g[i−1]f[i]=p∗g[i]+(1−p)∗g[i−1]
g[i]=q∗f[i]+(1−q)∗f[i−1]g[i]=q∗f[i]+(1−q)∗f[i−1]
忽然发现不就把(p,1−p),(q,1−q)(p,1−p),(q,1−q)分别对调一下么
什么时候拿走石头最优呢?若成功拿走石子的收益大于不拿走石子的收益者拿走,否则不拿走
对比f[i−1],g[i−1]f[i−1],g[i−1]即可
代码如下:
#include <bits/stdc++.h>
using namespace std;
int n;
double p,q;
double f[10005],g[10005];
void work() {
scanf("%d%lf%lf",&n,&p,&q);
n=min(n,10000);
g[0]=1;
for(int i=1;i<=n;i++) {
if(f[i-1]>g[i-1]) { p=1.0-p;q=1.0-q; }
double d=p+q-p*q;
f[i]=(p*g[i-1]+(1-p)*q*f[i-1])/d;
g[i]=(q*f[i-1]+(1-q)*p*g[i-1])/d;
if(f[i-1]>g[i-1]) { p=1.0-p;q=1.0-q; }
}
printf("%.6lf\n",f[n]);
}
int main() {
int t;
scanf("%d",&t);
while(t--) {work();}
return 0;
}