试题 算法训练 印章
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
本题思路如下:
将求解概率问题分解为求解每取一个印章时的概率问题。
如:印章种类为2,购买数量为3,在仅摸了一次时,总计有印章种类数为1的概率为100%;
在摸了两次时,种类为1的概率为50%,种类为2的概率也为50%。
此时可列下表:
个数/种类数 |
1 |
2 |
1 |
100% |
0 |
2 |
50% |
50% |
3 |
25% |
75% |
注意观察规律,每一行的概率和固定为100%不会变,且重点是:每一行的概率都由前一行的概率和这一次印章出现的概率所决定,记个数为i,种类数为j,概率为p,有以下推导公式:
p[i][j]=p[i-1][j-1]*(n-j)/n+p[i-1][j]*(j+1)/n
公式描述不够清晰,可以简单理解为,前一行的同列和前一行的前一列两个概率分别乘以得到另一种未拥有印章的概率。
需要提醒的是,当种类数为1时,只要个数大于0,那么概率始终为100%;当购买个数小于种类数时,概率始终为0。
那么解决完特殊情况,获得了推导公式,那么我们可以使用记忆搜索法来求解该问题:
n,m=list(map(int,input().split()))
x=[[0 for i in range(min(i+1,n))] for i in range(m)]
x[0][0]=1
i=0
j=0
for i in range(1,m):
for j in range(len(x[i])):
if(i-1>-1 and j-1>-1):
try:x[i][j]=x[i-1][j-1]*(n-j)/n+x[i-1][j]*(j+1)/n
except:x[i][j]=x[i-1][j-1]*(n-j)/n
else:
if(j+1==n):x[i][j]=x[i-1][j-1]*(n-j)/n
else:x[i][j]=x[i-1][j]*(j+1)/n
if(m<n):
print('0.0000')
else:
print('%.4f'%x[i][j])