问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据要求
m,n <=30
题解
一看数据量就猜测是dp的问题,数学问题的数据量一般很大。如果想到了dp,那么就好办了。
代码
package lanqiao.imporve;
import java.util.Arrays;
import java.util.Scanner;
/**
* @author: Zekun Fu
* @date: 2022/11/21 10:38
* @Description: 印章
*
* 1. dp[n][m]:表示买n枚,集齐m种印章的概率
*
* 当前这一种是一种新印章的概率 (n - j) / n
* 当前这一种是一种旧印章的概率 (j) / n
*
* 1. dp[i][j - 1] = dp[i - 1][j - 1] * ((j - 1) / n)
* -> dp[i][j] = dp[i - 1][j] * (j / n)
*
* 2. dp[i][j] = dp[i - 1][j - 1] * ((n - j + 1) / n)
*
* 综上
* dp[i][j] = dp[i - 1][j] * (j / n) + dp[i - 1][j - 1] * ((n - j + 1) / n)
*
* 每一种印章有两种选择
* 1. 抽到新的
* 2. 抽到旧的
*
* 可以大体看作是背包模型。
*
* 2 3的时候
*
* i == 1的时候
* 0 : 0
* 1 : 1
*
* i == 2的时候
* 1 : 1 * 0.5
* 2 : 0.5
*
* i 等于3的时候
* 1 = 1
* 2 = 0.5 + 0.5 * 0.5 = 0.75
*
*
*/
public class YinZhang {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
double[] dp = new double[n + 1];
Arrays.fill(dp, 0);
dp[1] = 1;
double d = (double)n;
for (int i = 2; i <= m; i++) {
double[] pre = Arrays.copyOf(dp, n + 1);
for (int j = 1; j <= Math.min(n, i); j++) {
dp[j] = pre[j - 1] * ((double) (n - j + 1) / d) + pre[j] * ((double)j / d);
}
}
System.out.printf("%.4f", dp[n]);
}
}
cpp代码
#include
#include
using namespace std;
// m种印章种
/*
f[i][j]:表示从i种印章中恰好选择j种印章的概率
f[m][n] = f[m - 1][n - 1]
1. i种印章中含有j种印章的第一种划分
当前选择的这个是已经选择过的印章
f[i - 1][j] * j/n
2. 当前选择的是一种新的印章
f[ - 1][j - 1] * (n - j + 1) / n;
3. 边界条件
i = 1的时候,f[1][j] = 1 / n;
i < j的时候,等于0
*/
const int N =1005;
double f[N][N];
int main() {
int m, n;
cin >> n >> m;
f[1][1] = 1.0;
for (int i = 2; i <= m; i++) {
for (int j = 1; j <= i; j++) {
f[i][j] = f[i - 1][j - 1] * (double)(n - j + 1) / n + f[i - 1][j] * (double) j / n;
//cout << f[i - 1][j] * (double) j / n << endl;
}
}
printf("%.4llf\n", f[m][n]);
}
#include
#include
#include
using namespace std;
// m种印章种
/*
f[i][j]:表示从i种印章中恰好选择j种印章的概率
f[m][n] = f[m - 1][n - 1]
1. i种印章中含有j种印章的第一种划分
当前选择的这个是已经选择过的印章
f[i - 1][j] * j/n
2. 当前选择的是一种新的印章
f[ - 1][j - 1] * (n - j + 1) / n;
3. 边界条件
i = 1的时候,f[1][j] = 1;
i < j的时候,等于0
*/
const int N = 25;
double f[2][N];
int main() {
int m, n;
cin >> n >> m;
f[1][1] = 1.0;
for (int i = 2; i <= m; i++) {
for (int j = 1; j <= i; j++) {
f[i & 1][j] = f[(i - 1) & 1][j - 1] * (double)(n - j + 1) / n + f[(i - 1) & 1][j] * (double) j / n;
//cout << f[i - 1][j] * (double) j / n << endl;
}
}
printf("%.4llf\n", f[m & 1][n]);
}