【JZOJ 3072】掷骰子

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+(m1m)21m+(m1m)41m+(m1m)61m...

i一次跳到了终点,或者第一次没中,j也没中,i才中,以此类推,

=1mi=0(m1m)2i

用等比数列表示:
=1m(m1m)11m22m

由于 (m1m) 无限接近0,也就是等于0,所以
=1mm22m1=m2m1

所以:
fi,j=m2m1

2、i在[n-m+1,n]这个区间内:

fi,j=1m+j+mk=j+1(m1)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(m1)fk,jm2

如果i在n-m这个位置,那么就有 1m 的概率直接跳到终点,但也有可能在跳之前j先到了终点,所以要减 m1m1m 等于 1m2
后面的与前面的差不多,这里就不多写了;

4、i,j都不在区间内:

这个简单嘛,直接把他们后面的概率加起来除 m2 即可,
fi,j=i+mk=i+1j+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值