您的位置:首页 > 其它

bzoj2318Spoj4060 game with probability Problem 期望DP

2017-09-27 22:41 435 查看
题意:n个石子,两个人选,分先手后手,A做自己想做的事情概率为p,B则为q,每次可以取一个石子或者不取。给出n,p,q问先手胜率,n<=1e9.

这么鬼畜的大小肯定矩阵乘法,矩阵乘法肯定就DP先,设f[i]表示还剩i个石头先手胜的概率,g[i]表示后手,那么我们递推一下。

先假设每个人都想要拿。

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)

底下除的那个玩意儿是1-两个都不能干自己想干的事情的概率,简而言之这个式子就是取或者不取石头以后转移状态除以所有事情的总概率。

后面的不选择的情况之所以要乘以另外一个人的概率是因为如果另外一个人如果不选我的状态仍然不会变,还是一样的。

然后问题是每个人不一定想拿,是否想拿取决于f[i-1]是否大于g[i-1]。

这就很鬼畜了,每一次的转移都判断上一次的状态,因为如果不想的话我还要p=1-p,q=1-q,这不可能矩阵乘法,然后我就歇菜了。

%一波题解,发现还有黑科技= =如果n特别大的时候,对于小数点前6位基本上没有影响,看来在适当范围内的trick还是有必要的啊,学习了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
int n,m;
typedef double db;
db f
,g
;
int main()
{
int cas;
scanf("%d",&cas);
while (cas--)
{
db p,q;
scanf("%d%lf%lf",&n,&p,&q);
f[0]=0,g[0]=1;
fo(i,1,min(n,1000))
{
if (f[i-1]>g[i-1])p=1-p,q=1-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));
if (f[i-1]>g[i-1])p=1-p,q=1-q;
}
printf("%.6lf\n",f[min(n,1000)]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: