【bzoj2318】game with probability
Description
Alice和Bob在玩一个游戏。有n个石子在这里,Alice和Bob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,同样,Bob有q的概率投掷出他相投的一面。
现在Alice先手投掷硬币,假设他们都想赢得游戏,问你Alice胜利的概率为多少。
答案保留6位小数
数据范围$1<=t<=50,0.5<=q,p<=0.99999999,1<=n<=99999999 $
Solution
额这题也是权限题放一下题面好了
这个的话一开始自己想多了。。老觉得会不会说为了最优决策所以Alice为了有更大的概率得到想要的结果而故意想相反的结果之类的。。
emmm后来发现Alice和Bob根本不知道概率是多少所以根本不存在这个问题qwq
那所以我们可以考虑直接两个数组大力dp
记\(f[i]\)为\(i\)个石子的情况下Alice(先手)的胜率,\(g[i]\)为\(i\)个石子的情况下Bob(后手)的胜率
那么我们可以得到这样的两种转移:
1、如果说这一步拿走石子会更优,那么有:
具体一点的话以\(f[i]\)的计算为例,前半部分是第一步Alice拿了,然后局面就变成了有\(i-1\)个石头并且是后手,所以乘的是\(g[i-1]\),然后后半部分就是第一步Alice没有拿,那就变成了有\(i\)个石头并且是后手,所以是乘上\(g[i]\),第二条式子同理就不赘述了
然后我们大力化一下可以将这两条式子变成用\(f[i-1]\)和\(g[i-1]\)推出\(f[i]\)和\(g[i]\)的式子
(用\(g[i]\)的表达式把第一条式子里面的\(g[i]\)换掉就好了,第二条式子的处理类似):
2、如果说这一步不拿走石子会更优,那么有:
具体含义什么的跟上面的差不多
仔细看一下emmmm那好像直接就是\(p\)变成了\(1-p\)然后\(q\)变成了\(1-q\)而已。。所以转移的时候判断一下就好了
那现在就是怎么判断是拿更优还是不拿更优,仔细思考一下这个拿和不拿其实影响到的是接下来局面的先手后手问题,所以我们就比较一下\(i-1\)个石子的情况下是先手的胜率更大还是后手的胜率更大就好了,也就是直接比较一下\(f[i-1]\)和\(g[i-1]\)即可
然后还有一个东西就是。。推大概到\(1000\)之后(额或者更小一点也行qwq貌似只推到\(50\)也。。问题不大?没有交过qwq)在精度范围内就不会有变化了,所以大力“近似”一下每次只用推到\(1000\),这样就不会有超时的问题啦(算是一个套路吗。。?)
代码大概长这个样子:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1001;
double f[N],g[N];
double p,q,P,Q;
int n,m,t;
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&t);
for (int o=1;o<=t;++o){
scanf("%d%lf%lf",&n,&P,&Q);
n=min(n,1000);
f[0]=0; 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;
else p=P,q=Q;
f[i]=(p*g[i-1]+(1-p)*q*f[i-1])/(1-(1-p)*(1-q));
g[i]=(q*f[i-1]+(1-q)*p*g[i-1])/(1-(1-p)*(1-q));
}
printf("%.6lf\n",f[n]);
}
}