题目描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
解题思路
动态规划最难的就是想出动态方程,这里我们定义的动态方程为: dp[i][j]
表示购买i
个印章,集齐了j
种的概率
- 首先我们最容易想到的是:当印章种类
n == 1
时,dp[i][j]
是必然为1的,因为当印章的种类为1时,无论买多少个印章,都是必然集齐的 - 另外,当
m < n
时,dp[i][j] = 0
,这是因为,当所购买的印章数量还没有印章本身的种类多时,是永远不可能集齐的。 - 当买i张,集齐1种的概率为
(1/n)^i * n
,即(1/n)^ (i-1)
,因为每次购买,获取特定印章的概率恒为(1/n)
,那么购买i次,概率就变成了(1/n)^i
,但是这只是考虑了1种印章的情况,一共有n种印章都要考虑进去,所以还要在之前的概率上乘以n:
就变成了(1/n)^i * n
上面讨论的是一些特殊情况,现在考虑一般的递推公式dp[i][j]
,这里又要分为2种情况:
1.买第i个印章时,买重复了
2.没有买重复
针对第1种情况,说明我们买的前 i-1
个印章已经集齐了 j
种,买的第 i
个徽章其实是这j种之一,所以:dp[i][j] = dp[i-1][j]*(j/n)
第二种情况,说明我们买的前i-1
个印章已经集齐了j-1
种,买的第i
个徽章刚好集齐了第j
种,所以: dp[i][j] = dp[i-1][j-1]*(n-(j-1))/n
代码
import java.util.Scanner;
public class Solution{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); //记录印章的种数
int m = sc.nextInt(); //记录购买的印章个数
double p = 1.0/n;
double[][] dp = new double[m+1][n+1]; //dp[i][j]表示买了i张,集齐j种的概率
if(n == 1){ System.out.printf("%.4f",1.0); return;}//如果印章种类为1,那么必中
if(m < n){ System.out.printf("%.4f",0.0);return; }//如果购买印章的个数还没有它的种类多,则必然不会集齐
for(int i =1; i<= m; ++i){//最外层是购买的印章个数遍历
for(int j=1; j<=n; ++j){//最内层是印章的种数
if(i < j)dp[i][j] = 0;
else if(j ==1){//买了i张,集齐1种的概率
dp[i][j]=Math.pow(p,i-1);
}else{
dp[i][j] = dp[i-1][j]*(j*1.0)/n+dp[i-1][j-1]*((n-j+1)*1.0/n);
}
}
}
System.out.printf("%.4f",dp[m][n]);
}
}