太郎和一只免子正在玩一个掷骰子游戏。有一个有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格.接下来是太郎掷骰子,太郎想知道他赢得比赛的概率就多少。
比赛的时候就切了,爽
第一眼看没啥头绪,看到算的是概率。
第二眼看好像概率很好算,就是前面m个数的和。
第三眼看发现反弹好像有后效性,然后发现可以滚动
第四眼看发现不会算答案…..
第五眼看还是不会算答案…..
……
第N眼(n>=100)看发现好像答案就是每一步到n的概率
第N+1眼看发现还要考虑b在之前到的情况
第N+2眼看发现直接乘上1-b先到的概率就是答案了
于是开始码码码,码的时候发现反弹可以直接镜像
有一个步数的问题,因为这个游戏是无限的,所以只要步数到了精度0.000001可以忽略不计的情况就可以直接退掉了, 大概就是15000
虽然题目看起来比较难,但是其实还是不复杂的。。。。
#include <cstdio>
#include <iostream>
#include <cstring>
typedef double ld;
using namespace std;
int n,m,x,y;
ld fx[2][4020],fy[2][4020],preX,preY,mm,ans,breach;
int main() {
cin>>n>>m>>x>>y;
fx[1][x]=fy[1][y]=1;
int o=0;
for (int i=1; i<=15000; i++) {
memset(fx[o],0,sizeof fx[o]);
preX=0;
preY=0;
for (int j=1; j<=n-1+m; j++) {
if (j-m-1>0 && j-m-1<n) {
preX-=fx[1-o][j-m-1];
}
fx[o][j]=preX/m;
if (j>0 && j<n) {
preX+=fx[1-o][j];
}
if (j-m-1>0 && j-m-1<n) {
preY-=fy[1-o][j-m-1];
}
fy[o][j]=preY/m;
if (j>0 && j<n) {
preY+=fy[1-o][j];
}
}
for (int j=n+1; j<=n-1+m; j++) {
fx[o][n-(j-n)]+=fx[o][j];
fy[o][n-(j-n)]+=fy[o][j];
}
ans+=(1-breach)*fx[o][n];
breach+=fy[o][n];
o=1-o;
}
printf("%.6lf",ans);
}