一开始入手,我打算用概率论的知识解决印章问题,但是发现我的公式推导,适应有一定的局限性
package com.study.蓝桥杯.算法训练;
/*
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
*/
/**
* @author sjn
* @date 2022-1-15
*/
import java.util.Scanner;
public class Main1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
double p = getProbability(m, n);
System.out.printf("%.4f", p);
}
/**
* @param m 小A买了m张印章
* @param n 一共有n种印章
* @return 小A集齐n种印章的概率
*/
//公式推导有局限性(@_@;)
//小A集齐n种印章的概率
public static double getProbability(int m, int n) {
//如果m<n,那么不可能集齐n种印章,即概率为0
if (m < n) {
return 0;
}
/*
概率公式P=(n!/n^m)*Cmn
*/
double pro = (getFactorial(n) * 1.0 / Math.pow(n, m)) * getFactorial(m) / (getFactorial(n) * getFactorial(m - n));
return pro;
}
//阶乘公式
public static long getFactorial(int num) {
long sum = 1;
for (int i = 1; i <= num; i++)
sum = sum * i;
return sum;
}
}
后来采用动态规划的的方法来解决这一类问题
package com.study.蓝桥杯.算法训练;
/*
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
*/
/**
* @author sjn
* @date 2022-1-15
*/
import java.util.Scanner;
public class Main1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
double p = dp(m, n);
System.out.printf("%.4f", p);
}
/**
* @param m 小A买了m张印章
* @param n 一共有n种印章
* @return 小A集齐n种印章的概率
*/
public static double dp(int m, int n) {
//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++) {
//因为i<j,不可能买了i张凑齐j种,所以dp = 0;
if (i < j) {
dp[i][j] = 0;
} else if (j == 1) {
//如果j=1 买了i张凑齐j种,所以dp=p的i-1次方
dp[i][j] = Math.pow(p, i - 1);
} else {
//其他情况,买了i张凑齐j种,第i张有两种情况,第一种和之前凑齐的一样,第二种和之前凑齐的不一样
dp[i][j] = dp[i - 1][j] * (j * p) + dp[i - 1][j - 1] * ((n - j + 1) * p);
}
}
}
return dp[m][n];
}
}