JZOJ3072. 【NOIP2012模拟10.31】掷骰子

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×(11m)2
    i第三次到达的概率应该是:i前两次没有到达的概率×j前两次没有到达的概率×i这一次到达的概率,即 1m×(11m)4
    i第n次到达的概率就是 1m×(11m)2n

    那么,在i这个位置的最终概率就应该把每一次到达的概率加起来, 1m+1m×(11m)2+1m×(11m)4+...+1m×(11m)2n
    1m 提出来: 1m×(1+(11m)2+(11m)4+...+(11m)2n)
    现在在中间计算一个等比数列,公比是 (11m)2 通过等比数列求和
    算出,原式= 1m×1(11m)2n+11(11m)2
    因为n趋于无限,所以 (11m)2n+1 趋于0。
    化简得出,原式= m2m1

所以 fi,j=m2m1(i,j)

  • 再考虑第二种情况:i在反弹区间,而j不在。

    因为i所在的区间的状态都一下的,所以除了i自己本身,就要(m-1)个相同状态,
    因为i有 1m 的概率到达终点,所以应该加上 1m
    fi,j=(m1)×j+ml=j+1fi,l+mm2

  • 第三种情况就与第二种相反,j在反弹区间,而i不在。

与上面类似,但需要注意的是:如果i在n-m位置,它就要 1m 的概率到达终点,不过需要减去j在上一次就到达n的概率
此时的 fi,j=(m1)×i+ml=i+1fl,j+1m2

  • 最后一种情况,就是i,j都不在。

这个转移很简单:
fi,j=i+ml=i+1j+mk=j+1fl,km2

  • 但是如果这样子会超时,我们就用 sumi,j 表示 nl=ink=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));
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值