试题 算法训练 印章
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
解题思路:
n种图案,m张印章,概率为p=1/n,这个题因为情况不一样,所以我们可以分情况来看。
首先:dp[i][j]就代表i张印章凑齐j种图案的概率
然后我们先来看一般情况:
1) i<j,就说明我们不可能凑齐,这个时候dp[i][j]=0
2) j=1,就说明我们买的i张印章里面,只要凑齐1种就行(注意是随便1种就可以了,就比如说abc三种,我们a算1种,b算1种,c算1种,概率计算的时候就算的是这三种概率的和 没理解的再想想...)
其中 j=1 的时候我们也可以分两种情况:一种就是 i=1,这个时候就相当于我们的概率dp[i][j]=1.另一种就是 i>1,我们每个图案的概率都是dp[i][1]= p^i;因为我们的图案不指定哪一种,所以我们的dp[i][j]是n种图案的概率之和,就是p^i*n;因为p=1/n;所以就是p^(i-1)
接着我们看其它的情况:
3) 因为我们的凑的印章不规定是哪一种图案,所以我们dp记录我们概率值的时候,就得分情况:要么是有重复的(j已经凑齐了),要么是没重复的(还有没凑齐的)
有重复的:表示前面的 i-1 张里面已经凑齐了 j 种,第 i 张就是重复前面的印章,这个时候前面已经出现过的 j 种印章再出现的时候,它就是 j*p
这个时候dp[i][j] = dp[i-1][j] * (j/n) = dp[i-1][j] *(j*p)
无重复的:表示前 i-1 张凑齐了 j-1 种,其中,第 i 张就是最后的一种要凑的,因为题目不规定具体求那一种图案的概率,所以我们只需要乘以前面没出现过的印章再出现的概率,因为前面已经出现了 j-1 种了,所以我们剩下没出现的就是 n-(j-1) 种,只要是其中的一种就行了,这个时候凑齐最后一种的概率就是 (n-j+1)*p
这个时候dp[i][j] = dp[i-1][j-1]*(n-(j-1))/n = dp[i-1][j-1]*(n-j+1)*p
我们只需要把这两种情况相加即可。
最后只需要输出我们要求的dp[m][n] 就可以了。
AC
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
double dp[30][30];
int main(void)
{
int n,m;
cin>>n>>m;
double p=1.0/n;
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)//i张印章
{
for(int j=1;j<=n;j++)//j种图案
{
if(i<j)//不可能凑齐
{
dp[i][j]=0;
}
if(j==1)//j只要所有图案中的一种就可以了,所以我们(1/n)^i还要再乘n,就是p^i-1
{
dp[i][j]=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;
}
}
}
printf("%.4lf\n",dp[m][n]);
return 0;
}