问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
思路
本题采用动态规划思想,自顶向下分解问题,自底向上求解问题,设一个double数组 dp[i][j],其中i表示第i次购买的印章,j为购买的印章中出现的j中印章,即dp[i][j]为购买i张印章出现j种印章的概率
-
若i<j,显然概率为0。
-
若j=1,即在i张印章中只出现1种印章,概率为(1/n)^(i-1)。
-
若j>1,则应该继续分解(自顶向下分解)
3.1 前面i-1张已经出现j种印章了,此时第i张印章只是重复前面的j种印章的其中一种 此时的概率为dp[i-1][j]*(j*1/n) 3.2 前面i-1张印章出现了j-1种印章,那么第i张为新出现的第j种印章,即(n-(j-1))种可能,故此时的概率为dp[i-1][j-1]*(n-(j-1))*1/n
所以对于这种情况dp[i][j]=dp[i-1][j](j1/n)+dp[i-1][j-1]*((n-(j-1))*1/n
此处思路来自大佬https://blog.csdn.net/qq_42368540/article/details/122211573****的文档
代码如下`
#include<iostream>
#include<iomanip>
#include<math.h>
#define Max 22
using namespace std;
double dp[Max][Max];
int main(){
int n,m;
double p;
cin>>n>>m;
p=1.00/n; //每种图章出现的概率
for(int i=1;i<=m;i++){ //购买的第i张印章
for(int j=1;j<=n;j++){ //每次购买印章可能出现的情况,分三种
if(i<j) dp[i][j] =0 ;//购买的印章张数小于出现的种类数,此时概率为0
if(j==1) dp[i][j]=pow(p,i-1); //只出现一种印章的概率
else dp[i][j]=dp[i-1][j]*(j*p)+dp[i-1][j-1]*((n-(j-1))*p); //即分别为两种情况,一种新购买的印章是前面重复的,另一种则是出现了新印章
}
}
cout<<setiosflags(ios::fixed)<<setprecision(4)<<dp[m][n]<<endl;
return 0;
}