蓝桥杯试题 算法训练 印章

Posted Alex_996

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝桥杯试题 算法训练 印章相关的知识,希望对你有一定的参考价值。

资源限制
内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s

问题描述
  共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
  
输入格式
一行两个正整数n和m

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

样例输入
2 3

样例输出
0.7500

数据规模和约定
1≤n,m≤20

题目链接

Ideas

首先要确定题目类型,虽然试题列表里面已经说明了这是一道DP的题目,但是正式比赛的时候可不会告诉你题目要用什么算法,因此要先分析一下用什么算法。

首先题目要求的是概率,但好像并没有什么算法是能够直接求概率的。我们可以转化一下思路,概率其实就是一个占比,符合要求的情况数量除以所有的情况数量。所以这个问题就等价为一个求情况数的问题,并且计算后面的情况数是要依赖于前面计算出来的情况数的,因此是一个状态转移的问题,要用动态规划。

动态规划解题步骤:

  1. 设置状态,即dp数组;
  2. 确定状态转移方程;
  3. 代码实现

首先第一步,设置状态,也就是要定义好dp数组。题目中有买的印章数和印章种类两个变量,因此要设置两个未知数,即二维数组dp[i][j]。dp数组的值可以直接定义为概率,也就是说,dp[i][j]表示买i个印章凑齐j种印章数的概率。

在确定状态转移方程之前还有一步,就是要确定边界情况,也就是要先确定一行或者一列,或者某几个值,确保状态转移方程的初始值是有的。

先来看i < j的情况,因为买i个印章最多只能凑齐i种,所以这种情况概率都为0。

再来看j = 1的情况,从最简单的情况开始分析,当i = 1, j = 1时,也就是我买1个印章凑齐1种的情况,肯定是可以的,所以dp[1][1] = 1

当i继续增加时,也就是买i张凑齐1种的概率,也就是i个印章都是同一种,概率是 ( 1 n ) i − 1 (\\frac1n)^i - 1 (n1)i1

这样我们就确定了第一列的值,初始状态也就确定了。

之后就要确定状态转移方程了,也就是确定中间状态dp[i][j],买的第i张,有两种状态,① 跟前面 i - 1 张有重复的,② 跟前面 i - 1 张没有重复的。

① 说明前面 i - 1 张已经凑齐了 j 种,这种情况的概率是 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∗ ( j n ) dp[i][j] = dp[i - 1][j] * (\\fracjn) dp[i][j]=dp[i1][j](nj)
② 说明前面 i - 1 张凑齐了 j - 1 种,这种情况的概率是 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] ∗ ( n − ( j − 1 ) n ) dp[i][j] = dp[i - 1][j - 1] * (\\fracn - (j - 1)n) dp[i][j]=dp[i1][j1](nn(j1))

确定了状态转移方程之后就可以开始写代码了。

Code

Python

if __name__ == '__main__':
    n, m = map(int, input().split())
    
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if j == 1:
                dp[i][j] = (1 / n) ** (i - 1)
            else:
                dp[i][j] = dp[i - 1][j] * (j / n) + dp[i - 1][j - 1] * ((n - j + 1) / n)
    print(f"dp[m][n]:.4f")

以上是关于蓝桥杯试题 算法训练 印章的主要内容,如果未能解决你的问题,请参考以下文章

朝题夕解——DP之印章

[蓝桥杯Python]算法练习算法基础算法训练算法模板(持续更新)

蓝桥杯试题 算法训练 数字游戏

蓝桥杯试题 算法训练 数字游戏

(蓝桥杯)试题 算法训练 回形取数

(蓝桥杯)试题 算法训练 排列