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格.接下来是太郎掷骰子,太郎想知道他赢得比赛的概率就多少。
Solution
用DP,
因为正着推不好玩,所以考虑一下倒推,
设
fi,j
表示两人分别在i和j,到i走,从终点开始倒推,
很显然,如果i=n,f直接加1,j=n直接跳过;
再分类讨论:
1、两人都在[n-m+1,n]这个区间内,i赢的概率是:
1m+(m−1m)2∗1m+(m−1m)4∗1m+(m−1m)6∗1m...
i一次跳到了终点,或者第一次没中,j也没中,i才中,以此类推,
=1m∑i=0∞(m−1m)2i
用等比数列表示:
=1m∗(m−1m)∞−11m2−2m
由于 (m−1m)∞ 无限接近0,也就是等于0,所以
=1m∗m22m−1=m2m−1
所以:
fi,j=m2m−1
2、i在[n-m+1,n]这个区间内:
fi,j=1m+∑j+mk=j+1(m−1)fi,km2
有可能直接跳到终点,或者从其他点跳来,对于每个的k,都有(m-1)个非终点跳到i,同时每个k刚好跳到j和每个非终点的跳到i概率积为 m2 ;
3、j在[n-m+1,n]这个区间内:
fi,j=(i+n=m?1:0)m2+∑i+mk=i+1(m−1)fk,jm2
如果i在n-m这个位置,那么就有 1m 的概率直接跳到终点,但也有可能在跳之前j先到了终点,所以要减 m−1m∗1m 等于 1m2 ,
后面的与前面的差不多,这里就不多写了;
4、i,j都不在区间内:
这个简单嘛,直接把他们后面的概率加起来除
m2
即可,
fi,j=∑i+mk=i+1∑j+ml=j+1fj,km2
我们发现每个
∑
可以写一个矩阵后缀和来优化,
答案就是
fx,y
。
复杂度: O(n2)
Code
成就:代码写进700!
#include<cstdio>
#include<cstdlib>
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef double db;
const int N=2050;
int m,n,x,y;
db f[N][N];
db S(int q,int w,int q1,int w1){return f[q][w]-f[q1][w]-f[q][w1]+f[q1][w1];}
int main()
{
scanf("%d%d%d%d",&n,&m,&x,&y);
db q=(1.0*m/(2*m-1));
fod(i,n,1)
fod(j,n,1)
{
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+m>n&&j+m>n)f[i][j]+=q;
else if(i+m>n)f[i][j]+=((m-1)*S(i,j+1,i+1,j+m+1)+m)/(m*m);
else if(j+m>n)f[i][j]+=((m-1)*S(i+1,j,i+m+1,j+1)+(i+m==n))/(m*m);
else f[i][j]+=S(i+1,j+1,i+m+1,j+m+1)/(m*m);
}
printf("%.6lf\n",S(x,y,x+1,y+1));
return 0;
}