【NOIP2012模拟10.31】掷骰子

31 篇文章 0 订阅
2 篇文章 0 订阅

题目

太郎和一只免子正在玩一个掷骰子游戏。有一个有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格.接下来是太郎掷骰子,太郎想知道他赢得比赛的概率就多少。

分析

fi,j 表示太郎在i,兔子在j,太郎的胜率。我们从后往前转移。
我们分四种情况:

1i+m<=n and j+m<=n
2i+m>n and j+m<=n
3i+m<=n and j+m>n
4i+m>n and j+m>n

why?
因为发现,当 i+m>n 时,i怎么跳总是 i+m>n ,那么就可以把它们当做同一种状态。j也一样。

情况一:i+m<=n and j+m<=n

因为i走到k的概率为 1m ,j走到l的概率也为 1m
那么状态(i,j)的胜率就是状态(k,l)胜率的总和。

fi,j=1m2k=i+1i+m+1l=j+1j+m+1fk,l

情况二:i+m>n and j+m<=n

fi,j=(m1)j+m+1l=j+1fi,lm2+1m

显然i有 1m 的概率到达终点,而i有m种可能,又那么既然已经算了到达终点的概率,那么就不用在计算,所以乘以(m-1)。

情况三:i+m<=n and j+m>n

fi,j=(m1)i+m+1k=i+1fk,jm2+x(i=nmx=1x=0)m

同样j有m种可能,但不能让他到达终点,那么就不用在计算,所以乘以(m-1)。
而当i在n-m这个位置时,i也有 1m 的概率到达终点,有可能出现状态(n,n),由于太郎是先手,所以算太郎赢,而前面有减去了j到达终点的情况,所以加上去。

情况四:i+m>n and j+m>n

要求i赢,所以
当i第一回合就走到了n,概率为 1m
当i第一回合没有走到n,而j也不能走到n,在第二回合i走到了n概率为 1m11m2
如果在第二回合i还是没有走到n,而j还是不能走到n,在第三回合i走到了n概率为 1m11m4
如此类推,

fi,j=limitn1m+1m11m2+1m11m4+...+1m11m2n

=1m1+11m2+11m4+...+11m2n

等比数列求和
=1m1[1(11m2n]111m2

因为数列的公比小于1, [1(11m2n] 无限趋近于1,所以
=1m1111m2

解得
fi,j=m2m1

但是这样是 O(n4) 的,用矩阵后缀和优化,变成 O(n2)

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
const int mo=1000000007;
const int N=2005;
using namespace std;
double f[N][N],n,m,x,y;
double val(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);
    for(int i=n;i>=1;i--)
        for(int j=n;j>=1;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+m>n && j+m>n)
                f[i][j]+=m/(2*m-1);
            else
            if(i+m>n)
                f[i][j]+=val(i,j+1,i+1,j+m+1)*(m-1)/m/m+1/m;
            else
            if(j+m>n)
                f[i][j]+=(m-1)*val(i+1,j,i+1+m,j+1)/m/m+(i==n-m)/m/m;
            else
                f[i][j]+=val(i+1,j+1,i+m+1,j+m+1)/(m*m);

        }
    printf("%.6lf",val(x,y,x+1,y+1));
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值