试题 算法训练 印章

问题描述

  共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。

输入格式

  一行两个正整数n和m

输出格式

  一个实数P表示答案,保留4位小数。

样例输入

2 3

样例输出

0.7500

数据规模和约定

  1≤n,m≤20

---------------------------------------------------------------------------------------------------------------------------------

首先考虑简单情况,当m<n,即 买的印章个数<印章种类 时,集齐n种印章的概率为0;

当印章种类个数n为1时,无论买了几张印章(m>=1),集齐的概率都为1;

接下来使用动态规划的思想,以m=4,n=3为例,设置数组dp[3][4];

每种印章抽到的概率为P = \frac{1}{n};(此处为  \frac{1}{3} )

dp[0][0]表示用1张印章集齐1种印章的概率:种类可以为n(此处为3)种中的任意一种,每种抽到的概率为P,则dp[0][0]=n*P=1;

dp[1][0]表示用2张印章集齐1种印章的概率:第1张印章的种类为3种中的任意一种,第2张印章的种类与第1张印章的种类相同,dp[1][0]=n*p*p=P

dp[i][0]表示用i+1张印章集齐1种印章的概率:dp[i][0]=P^{i}

dp[1][1]表示用2张印章集齐2种印章的概率:第1张印章的种类为n(此处为3)种的任意一种,第2张印章的种类为剩下n-1(此处为2)种印章的任意一种,则dp[1][1]=(n*P)*[(n-1)*P]=dp[0][0]*(n-1)*P

dp[2][2]表示用3张印章集齐3种印章的概率:第1张印章的种类为n(此处为3)种的任意一种,第2张印章的种类为剩下n-1(此处为2)种印章的任意一种,第3张印章的种类为n-2(此处为1)种的任意一种,则dp[2][2]=(n*P)*[(n-1)*P]*[(n-2)*P]=dp[1][1]*(n-2)*P

dp[i][j](i==j)表示用i+1张印章集齐i+1种印章的概率:第1张印章的种类为n种的任意一种,第2张印章的种类为剩下n-1种印章的任意一种,.....以此类推第i+1张印章的种类为n-i种的任意一种,则dp[i][j]=dp[i-1][j-1]*(n-i)*P

dp[2][1]表示用3张印章集齐2种印章的概率:分为两种情况,第一种情况为前2张印章已经集齐了2种印章,则第3张印章的种类可以为该2种的任意一种,该概率为dp[1][1]*2*P;第二种情况为前2张印章只集齐1种印章,则第3张印章的种类为剩下n-1(此处为2)种印章的任意一种,该概率为dp[1][0]*(n-1)*P;因此,dp[2][1]=dp[1][1]*2*P+dp[1][0]*(n-1)*P

dp[i][j](i!=j)表示用i+1张印章集齐j+1种印章的概率:第一种情况为前i张印章已经集齐j+1种印章,此时第i+1张印章的种类可以为该j+1种印章的任意一种,该情况下的概率为dp[i-1][j]*(j+1)*P;第二种情况为前i张印章仅集齐j种印章,则第i+1张印章的种类为剩下的n-j种的任意一种,该情况下的概率为dp[i-1][j-1]*(n-j)*P;因此,dp[i][j]=dp[i-1][j]*(j+1)*P+dp[i-1][j-1]*(n-j)*P

最终买m张印章,集齐n种印章的概率为dp[m-1][n-1]

张数/种数123
1100
21/32/30
31/92/9
41/27

#include <iostream>
#include <cmath>
#include<iomanip>
using namespace std;
double dp[25][25], p;
int main()
{
	
	int n,m;
	cin>>n>>m;
	p = 1.0 / n; //每种出现的概率
	for ( int i = 0; i < m; i++ ) {
		for ( int j = 0; j < n; j++ ) {
			if ( i <  j ) dp[i][j] = 0;
			if(i==0&&j==0)
			{
				dp[i][j]=1;
			}
			else if ( i!=0&&j == 0 ) {
				dp[i][j] = pow (p, i);  //p^(i)
			}
			else if(i==j){
				dp[i][j] =  dp[i-1][j-1] * ((n-i)*p);
			}
			else
			{
				dp[i][j]=dp[i-1][j]*(j+1)*p+dp[i-1][j-1]*(n-j)*p;
			}
		}
	}

	cout<<fixed<<setprecision(4)<<dp[m-1][n-1];
	return 0;
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值