首先要理解题意
题意是说Alice和Bob能投出他们想要的那一面的p和q,不是只能投出1...
也就是说他们可以根据场上情况来决策p/q的概率来投出1还是0
我们来倒着定义状态数组,逆推
$f_i$表示场上还有i颗石子,在这一轮Alice先手赢的概率
$g_i$表示场上还有i颗石子,在这一轮Alice后手赢的概率
"这一轮"的意思是当前一直没人投出1,一旦有人投出1,这一轮结束
(1)当$f_{i-1}>g_{i-1}$时
Alice希望这一轮Bob拿到石子,这样她在下一轮才能先手,所以Alice在这一轮不想拿,Bob也不想拿
当Alice先手时,$f_i=g_{i-1}*(1-p)+g_i*p$
当Alice后手时,$g_i=f_{i-1}*(1-q)+f_i*q$
由于他们都不想拿,所以p/q都表示他们投出0
这样就好理解了
(2)当$f_{i-1}<g_{i-1}$时
与(1)差不多
$f_i=g_{i-1}*p+g_i*(1-p)$
$g_i=f_{i-1}*q+f_i*(1-q)$
然后经过代入化简,就可以线性递推了
然后n很大,但是当n到一定程度后,概率就会趋近于某个值,对于1e-6的精度,n推到1e4就足够了
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int #define dd double using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } int T; int n; dd p,q,g[10006],f[10006]; int main(){ //freopen("in.in","r",stdin); rint i; read(T); while(T--) { read(n); scanf("%lf%lf",&p,&q); n=min(n,10000); f[0]=0; g[0]=1.0; for(i=1;i<=n;++i) { if(f[i-1]>g[i-1]) { f[i]=((1.0-p)*g[i-1]+p*(1.0-q)*f[i-1])/(1.0-p*q); g[i]=((1.0-q)*f[i-1]+q*(1.0-p)*g[i-1])/(1.0-p*q); } else { f[i]=(p*g[i-1]+q*(1.0-p)*f[i-1])/(1.0-(1.0-p)*(1.0-q)); g[i]=(q*f[i-1]+p*(1.0-q)*g[i-1])/(1.0-(1.0-p)*(1.0-q)); } } printf("%.6lf ",f[n]); } }