原题:
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模和约定
1≤n,m≤20
解题思路:
题目很简单这里就不再读了,这道题目涉及到的算法是动态规划(dp),按照动态规划的一般步骤来解:
1.对算法进行抽象——进行操作和状态的设计
动态规划常见的套路就是通过上一次操作的状态得这一次操作得状态,想要找到状态先要找到操作,操作就是买印章,接下来就是要找到状态,本题要求的就是概率那么这一道题的状态很大可能就是概率了,动态规划的尿性就是从第一次操作开始就会对最后的结果产生影响,那么我们就从第一次操作开始找状态(概率),第一次买印章的肯定可以获得一种印章最多也只能获得一种印章,那么第二次买印章可能会获得一种印章也可能获得两种印章,第三次操作可能会获得一种、两种、三种印章(先抛开题干 不受印章种类的限制),那么我们不妨建立一个二维数组,列( j )表示已经获得的印章种类数,行( i )表示进行的购买印章的次数,对应的数值就是i次买印章能获得j种印章的概率,第i行第j列的意思就是购买i次获得j种印章的概率。
2.找到状态转移方程
第i次获得j种印章事件发生最多有以下两种情况(也就是说有的时候只有一种情况例如 j = 1):
第一种情况第i-1次购买时已经j种印章,第i次购买买到了之前获得过的印章。
第二种情况第i-1此购买时获得了j-1种印章,第i次购买买到了之前没有获得过的印章。
由此可以得到状态转移方程。
3.确定初始状态
第一次购买能够获得一种印章的概率为1
4.执行操作进行状态转移
是否购买到新的印章收到列数j的影响,可以将购买分成三种情况:
①一直都买到的是一种,只具有状态转移的第一种情况(j == 1 && i != 1)
dp[i][j] = dp[i-1][j]/n*1.0;//状态转移
②在这一次购买种可能买到了已经拥有的印章,也可能购买到未拥有的印章(j < i)
dp[i][j] = dp[i-1][j]*(j*1.0)/n+dp[i-1][j-1]*(n-j+1)/n*1.0;//状态转移
③在这一次购买一定买到了未拥有的印章(j == i)
dp[i][j] = dp[i-1][j-1]*(n-j+1)/n*1.0;//状态转移
5.找到答案
输出dp数组种第m行第n列的数据(dp[m][n]的意思就是购买了m次买到了n种不同的印章)
代码部分:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
double dp[25][25];//dp[i][j],买i次获得j种不同印章的概率;
cin >> n >> m;
if(n > m)
printf("0.0000");//种类数多于购买次数,概率为零;
else
{
//开始模拟;
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= i && j <= n; j++)
{
if(j == 1)
{
if(i != 1)
dp[i][j] = dp[i-1][j]/n*1.0;//状态转移①:一直买到一种印章;
else
dp[i][j] = 1.0;//建立初始状态;
}
else if(j < i)
{
dp[i][j] = dp[i-1][j]*(j*1.0)/n+dp[i-1][j-1]*(n-j+1)/n*1.0;//状态转移②:可能买到已经拥有的印章也可能拥有新的印章
}
else
dp[i][j] = dp[i-1][j-1]*(n-j+1)/n*1.0;//状态转移③:一定买到新的印章
}
}
printf("%.4f",dp[m][n]);//控制输出;
}
return 0;
}