问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
资源限制
时间限制:1.0s 内存限制:256.0MB
解题思路:
该题目中采用动态规划思想,即一个多阶段问题一般由初始状态开始。题意为从总共有的 n 种中抽取 m 种,求集齐 n 种牌的概率。定义一个二维数组 arr[m + 1][n + 1] ,为了使得抽取个数的下标从 1 开始,故将数组中下标为 0 的元素闲置不用,故数组一维长度定义为 m + 1 和 n + 1。定义变量 i 对行号进行遍历,变量 j 对列号进行遍历,当 i < j 时该位置代表的概率(抽取 i 次集齐 j 种)为 0 。若 j 值为 1,故表示的意思是共抽取的 i 次但是只集齐了 1 种,故此时的概率是 * n,这里乘以 n 的原因是集齐的这 1 种有 n 种选择。对于其余情况的概率计算:该位置的上一行、同列位置的基础上再抽取一次(i 加一),此时抽取到的是原集齐的 j 种中的一种(j 不变);第二种情况是该位置的上一行、上一列位置的基础上再抽取一次(j 加一),此时抽取到的是除了原集齐的 j 种之外的其他种牌(j 加一),这个时候的概率计算如代码中所示。该算法的Java实现如下:
import java.text.DecimalFormat;
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
double p = 1.0 / n;
double[][] dp = new double[m + 1][n + 1];
DecimalFormat df = new DecimalFormat("0.0000");
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) {
dp[i][j] = Math.pow(p, i - 1);
} else {
dp[i][j] = dp[i - 1][j] * (j * p) + dp[i - 1][j - 1] * ((n - j + 1) * p);
}
}
}
System.out.println(df.format(dp[m][n]));
}
}