题目描述
最近西雅图的高中校园里流行这样一个游戏。
我们有一个骰子,这个骰子有 M 个面,分别写着 1..M ,并且是个公平的骰子,换句话说,一次投掷时每个面朝上的概率是相同的。
游戏的组织者使用这个骰子进行 N 次投掷,并且告诉玩家点数 v 出现了至少一次。那么玩家需要猜测 N 次投掷的点数之和。如果猜对了,就赢得了这个游戏。
小宇也喜欢玩这个游戏。在一次游戏中,她猜测了一个正整数 sum ,于是她想知道猜对的概率是多少。
输入格式
输入文件仅一行,包括 4 个正整数 N, M, v, sum 。
输出格式
输出文件包括一行,一个实数,保留 8 位小数,表示猜对的概率。
样例数据 1
输入
2 6 6 12
输出
0.09090909
分析:定义f[i][j][k],表示猜了i次,和为j,是否猜到过v,于是不难看出转移:
f[i][j][k==v]+=f[i-1][j-k][0]/(m*1.0);
f[i][j][1]+=f[i-1][j-k][1]/(m*1.0);
最后统计和为sum的概率/总的和的概率就得到答案:
# include <iostream>
# include <cstdio>
# include <cmath>
# include <list>
# include <cstring>
# include <map>
# include <ctime>
# include <algorithm>
# include <queue>
using namespace std;
typedef long long ll;
int read(){
int f=1,i=0;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return f*i;
}
double tot,f[55][2550][2];
int n,m,v,sum;
int main()
{
n=read(),m=read(),v=read(),sum=read();
f[0][0][0]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=n*m;++j)
for(int k=1;k<=min(j,m);++k)
{
f[i][j][k==v]+=f[i-1][j-k][0]/(m*1.0);
f[i][j][1]+=f[i-1][j-k][1]/(m*1.0);
}
for(int i=1;i<=n*m;++i) tot+=f[n][i][1];
printf("%.8f\n",f[n][sum][1]/tot);
}