题目描述:
资源限制
内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
解题方法:
动态规划五部曲:
①确定dp数组的含义:
dp[m][n],表示买m张印章凑齐n种的概率
②dp数组数组的递推公式:
p=1/n;对应的概率
有两种情况组成dp[i][j],
1.在买当前第i张之前,也就是已经买了i-1张,此时已经凑到了j种,再抽一张还是j种,其对应的概率为j*p,dp[i][j]=dp[i-1][j]*j*p;
2.在买当前第i张之前,也就是已经买了i-1张,此时只凑到了j-1种,再抽一张才是j种,其对应的概率为[n-(j-1)]*p,dp[i][j]=dp[i-1][j-1]*(n-j+1)*p;
那么,dp[i][j]的结果就是这两个结果的和:
dp[i][j]=dp[i-1][j]*j*p+dp[i-1][j-1]*(n-j+1)*p;
③dp数组的初始化:
1)当买的印章数<印章的种数,i<j,那么其对应的概率为0,因为只买i张,不可能凑到j种
此时dp[i][j]=0;
2)当印章种数为1,也就是j=1,那么买一张是1种的概率为1,买第二张还是1种的概率为p,因为要保证买到的第二张和第一张是一样的,所以概率为p
此时dp[i][j]=Math.pow(p,i-1);
④dp数组的遍历顺序:
外层遍历张数,内层遍历印章的种类
⑤测试验证
import java.util.*;
public class _Main{
/**
* 共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
**/
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
//定义p代表每一次的概率
double p = 1.0 / n;
//定义dp数组 dp[m][n] 买m张凑齐n种的概率
double[][] dp = new double[m + 1][n + 1];
//i代表买了i张 j代表凑齐j种
for (int i = 1; i <=m; i++) {//外层遍历买了多少张
for (int j = 1; j <=n; j++) {//内层遍历对应的多少种
//初始化dp数组
//如果数量<种类,那么肯定是凑不齐的,那么对应的概率为0
if (i<j)
dp[i][j]=0;
if (j==1)//全部的印章只凑一种
dp[i][j]=Math.pow(p,i-1);//第一次取到1种的概率为1,第二次取到和第一张一样的概率为p
else {
/**
* 两种情况:
* ①再抽第i张牌之前,已经凑到了j种,再抽一张凑到j种的概率为j*p,dp[i][j]=dp[i-1][j]*(j*p)
* ②再抽第i张牌,刚好凑到了j种,说明手里面的牌为j-1种,剩下的种数为[n-(j-1)]种,再抽一张凑到j种的概率为[n-(j-1)]*p,dp[i][j]=dp[i-1][j-1]*(n-j+1)*p
*/
dp[i][j]=dp[i-1][j]*(j*p)+dp[i-1][j-1]*(n-j+1)*p;
}
}
}
System.out.printf("%.4f",dp[m][n]);
}
}
输出结果正确。