【JZOJ 3072】 掷骰子

58 篇文章 0 订阅
37 篇文章 0 订阅

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格.接下来是太郎掷骰子,太郎想知道他赢得比赛的概率就多少。

100%的数据.10≤n≤ 2000,1≤m,x,y≤n-1

Analysis

很好的一道题,2^1024个好评,推荐大家做做。
初二一群人水法水过,暴力走15000步递推算答案卡精度卡时间竟然给卡过了?!无力吐槽。(infleaking同学(不是vfleaking神犇啊(%%%),大家不要看错了)还高举“能过的方法就是好方法”、“水法也是好方法”的旗帜。。。)
好了,扯了这么多,这些个人见解到时候在集训总结里写吧,回到正题。

DP

f[i][j] 表示A在i,B在j,A先走并获胜的概率。
边界的话如果 i=n ,显然 f[i][j]=1
如果 j=n ,显然 f[i][j]=0
有人要问了,如果 i=j=n f[i][j]=?
看看 f 的定义,由于当前是A走,所以一步之前是B走,两步之前是A走,所以A在两步之前就到达了n,所以 f[n][n]=1
那么,剩下的就是转移了。
我们分类讨论。

Case 1 : i<=n-m,j<=n-m

这部分没什么限制,直接dp

f[i][j]=i=i+1i+mj=j+1j+mf[i][j]1m2

Case 2:i>n-m,j>n-m

他们一旦走超过n-m,因为有反弹,他们的位置始终会在区间 [nm+1,n] 内。
且除非有一个人走到了 n ,他们可能无穷地走下去。
这段区间内,所有点走到终点的概率都是1m
那A先走且获胜的概率就是

limn+i=0n(11m)2i1m

这就是个等比数列嘛,上求和公式
=1m1(11m)+1(11m)2

中间过程自己化简,最后就是
=m2m1

Case 3 : i>n-m,j<=n-m

i 只有1种可能上次从n转移过来,i共有m种可能,同时 j 也有m种取值,总共有 m2 种可能情况,区间内所有点都可视为一样的。
所以

f[i][j]=(m1)(j+mj=j+1f[i][j])+mm2

Case 4 : i<=n-m,j>n-m

类似Case3

f[i][j]=(m1)(i+mi=i+1f[i][j])+(i==nm)m2

i=nm ,当 i=j=n 时概率为1
当然,dp可以累加到二维前缀和上,这样单次状态转移就不需要 O(n2) 的时间。

Code

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
typedef double db;
const int N=2010;
db sum[N][N];
db S(int x1,int y1,int x2,int y2)
{
    return sum[x1][y1]-sum[x1][y2]-sum[x2][y1]+sum[x2][y2];
}
int main()
{
    int n,m,x,y;
    scanf("%d %d %d %d",&n,&m,&x,&y);
    db t=m*1.0/(m+m-1);
    fd(i,n,1)
        fd(j,n,1)
        {
            sum[i][j]=sum[i+1][j]+sum[i][j+1]-sum[i+1][j+1];
            if(i==n)
            {
                sum[i][j]++;
                continue;
            }
            if(j==n) continue;
            if(i>n-m && j>n-m) sum[i][j]+=t;
            else
            if(i>n-m) sum[i][j]+=S(i,j+1,i+1,j+m+1)*(m-1)/(m*m)+1.0/m;
            else
            if(j>n-m) sum[i][j]+=(S(i+1,j,i+m+1,j+1)*(m-1)+(i==n-m))/(m*m);
            else sum[i][j]+=S(i+1,j+1,i+m+1,j+m+1)/(m*m);
        }
    printf("%.6lf",S(x,y,x+1,y+1));
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值