蓝桥杯试题 算法训练 印章
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的题目,但是正式比赛的时候可不会告诉你题目要用什么算法,因此要先分析一下用什么算法。
首先题目要求的是概率,但好像并没有什么算法是能够直接求概率的。我们可以转化一下思路,概率其实就是一个占比,符合要求的情况数量除以所有的情况数量。所以这个问题就等价为一个求情况数的问题,并且计算后面的情况数是要依赖于前面计算出来的情况数的,因此是一个状态转移的问题,要用动态规划。
动态规划解题步骤:
- 设置状态,即dp数组;
- 确定状态转移方程;
- 代码实现
首先第一步,设置状态,也就是要定义好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)i−1。
这样我们就确定了第一列的值,初始状态也就确定了。
之后就要确定状态转移方程了,也就是确定中间状态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[i−1][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[i−1][j−1]∗(nn−(j−1))。
确定了状态转移方程之后就可以开始写代码了。
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")
以上是关于蓝桥杯试题 算法训练 印章的主要内容,如果未能解决你的问题,请参考以下文章