Description
太郎和一只免子正在玩一个掷骰子游戏。有一个有N个格子的长条棋盘,太郎和兔子轮流掷一个有M面的骰子,骰子M面分别是1到M的数字.且掷到任意一面的概率是相同的.掷到几.就往前走几步.当谁走到第N格时,谁就获胜了。游戏中还有一个规则“反弹”.就是当一位选手要走到第N格外时.他就会后退(就像飞行棋进营一样)。
假设现在一位追手在A格.当他掷出B时:
1.A+B<N,走到第A+B.络,
2.A+B=N,走到第N格,获胜。
3.A+B≥N,走到第(N-(A+B-N)格
现在太郎和兔子分别在第x和y格.接下来是太郎掷骰子,太郎想知道他赢得比赛的概率就多少。
Input
一行四个整数N,M,x,y。
Output
一行一个小数.表示太郎获胜的概率。(保留6位小数)
Sample Input
10 6 1 1
Sample Output
0.541725
Data Constraint
30%的欺据,l0≤n≤100。
100%的数据.10≤n≤ 2000,1≤m,x,y≤n-1
分析
我们设 fi,j 表示太郎在位置i,兔子在位置j,太郎获胜的概率。
- 我们应该分4种情况讨论。
我们先考虑在在反弹区间里面的情况,即在区间[n-m+1,n]里面,
在这个区里面到达终点的概率都是一样的。
如果太郎和兔子都在这个区间里面,那么他们是永远走不出这个区间的,
因为无论怎样退后,都不会退到n-m这个位置。
那么概率怎样算呢?
i直接到达的概率是 1m
i第二次到达的概率应该是:i第一次没有到达的概率×j第一次没有到达的概率×i这一次到达的概率,即 1m×(1−1m)2
i第三次到达的概率应该是:i前两次没有到达的概率×j前两次没有到达的概率×i这一次到达的概率,即 1m×(1−1m)4
i第n次到达的概率就是 1m×(1−1m)2n那么,在i这个位置的最终概率就应该把每一次到达的概率加起来, 1m+1m×(1−1m)2+1m×(1−1m)4+...+1m×(1−1m)2n
将 1m 提出来: 1m×(1+(1−1m)2+(1−1m)4+...+(1−1m)2n)
现在在中间计算一个等比数列,公比是 (1−1m)2 通过等比数列求和
算出,原式= 1m×1−(1−1m)2n+11−(1−1m)2
因为n趋于无限,所以 (1−1m)2n+1 趋于0。
化简得出,原式= m2m−1
所以 fi,j=m2m−1(i,j满足它们都在在反弹区间里面)
再考虑第二种情况:i在反弹区间,而j不在。
因为i所在的区间的状态都一下的,所以除了i自己本身,就要(m-1)个相同状态,
因为i有 1m 的概率到达终点,所以应该加上 1m 。
fi,j=(m−1)×∑j+ml=j+1fi,l+mm2第三种情况就与第二种相反,j在反弹区间,而i不在。
与上面类似,但需要注意的是:如果i在n-m位置,它就要
1m
的概率到达终点,不过需要减去j在上一次就到达n的概率
此时的
fi,j=(m−1)×∑i+ml=i+1fl,j+1m2
- 最后一种情况,就是i,j都不在。
这个转移很简单:
fi,j=∑i+ml=i+1∑j+mk=j+1fl,km2
- 但是如果这样子会超时,我们就用 sumi,j 表示 ∑nl=i∑nk=jfl,k
通过二维后缀和的维护,时间复杂度就变为 O(N2)
code(c++)
#include<cstdio>
#include<cstring>
#include<algorithm>
double f[2008][2008],n,m,x,y,z;
double g(int x1,int y1,int x2,int y2){return f[x1][y1]-f[x1][y2]-f[x2][y1]+f[x2][y2];}
int main()
{
scanf("%lf%lf%lf%lf",&n,&m,&x,&y);z=m/(2*m-1);
for(int i=n;i;i--)
for(int j=n;j;j--)
{
f[i][j]=f[i+1][j]+f[i][j+1]-f[i+1][j+1];if(i==n){f[i][j]++;continue;}
if(j==n)continue;if((i>n-m)&&(j>n-m))f[i][j]+=z;else
if(j>n-m)f[i][j]+=((m-1)*g(i+1,j,i+m+1,j+1)+(i==n-m))/(m*m);else
if(i>n-m)f[i][j]+=((m-1)*g(i,j+1,i+1,j+m+1)+m)/(m*m);else f[i][j]+=g(i+1,j+1,i+1+m,j+1+m)/(m*m);
}
printf("%.6lf\n",g(x,y,x+1,y+1));
}