目录
题意
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m。
输出格式
一个实数P表示答案,保留4位小数。
解题思路分析
购买m张印章,集齐n种印章的概率,显然是一道动态规划题。
按动归五部曲来:
第一步 定义dp数组
采用二维数组dp[i][j],定义为购买i张印章时,集齐了j种印章的概率。
因此在定义数组长度时,需要是(m+1)*(n+1)
(因为数组索引从0开始)
第二步 确定dp数组递推公式
在购买第i张印章时,集齐j种印章会有两种情况, 重复购买后集齐j种印章,和不重复购买后集齐j种印章。
- 重复购买:买到旧的印章(已经收集过的),那么在购买第i-1张时,就已经集齐了j种印章概率为dp[i-1][j] * (j*1.0/n)
- 不重复购买:买到新的印章(之前没收集过的),那么在购买第i-1张时,集齐了j-1种印章,概率为dp[i-1][j-1] *((n-(j-1))*1.0/n)
那么dp[i][j]的概率就为两个概率加起来,递推公式为
dp[i][j] = dp[i-1][j] * (j*1.0/n) + dp[i-1][j-1] * ((n-(j-1))*1.0/n)
第三步 dp数组的初始化
- dp[1][1] = 1,购买第一张印章时必定集齐一种印章。
- 当购买印章数目i 小于 集齐印章数目j时,概率必定为0
- 当集齐印章数目j 等于1时,说明一直重复抽到同一种印章,概率为(1.0/n)^ j
第四步 遍历顺序
一张一张买,显然i是从小到大递增,j亦然。
第五步 举例说明
没啥可举例的哈。
最终代码如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
double[][] dp = new double[m+1][n+1];
double p = 1.0/n;
//dp数组的初始化
dp[1][1] = 1;
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
if(i < j) dp[i][j] = 0; //初始化
if(j == 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); //递推过程
}
}
double P = dp[m][n];
System.out.printf("%.4f",P);
}
}
收获
- 蓝桥杯的输入格式,一行n和m两个整数,实际上也就是用两个scanner.nextInt()进行赋值就行了,不用管输入中间的那个空格。
- int型 * 1.0转化为double型
- Math.pow(x,y)函数,第一个参数为底数,第二个参数为指数。
- 输出保留四位小数的数字,采用格式化输出,跟c语言一致,System.out.printf(".4f",P); 注意是 printf 函数不是 printfln 函数。