✅
一、题目总述
● 题目难度:⭐️⭐️⭐️⭐️⭐️
二、题目解析
● 题目读起来很简单,仿佛做起来也很简单。但实际上费脑子。
● 算法思路——动态规划:
① 设置状态:用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 来表示 “买了第 i
张印章后,已经凑齐了其中 j
种的概率”。注意,这里面的 “j
种” 的意思不是 “凑齐了 1
、2
、…、j
这几个种” 的意思,而是 “对于 n
种印章来说,凑齐了其中的 j
个种(至于具体是哪 j
种,倒先不管)”。
② 设置状态转移方程:注意一些前提,其中
1
≤
i
≤
M
1≤i≤M
1≤i≤M、
1
≤
j
≤
N
1≤j≤N
1≤j≤N。且买到某种印章的概论为
p
=
1
N
p = \frac{1}{N}
p=N1。
[1] 当 i < j
的时候,即是说,买的张数 m
小于印章的种数 n
,显然无论如何都不能凑齐,则此时:
d
p
[
i
]
[
j
]
=
0
dp[i][j]=0
dp[i][j]=0
[2] 当 i ≥ j 且 j = 1
的时候,即是说,买了第 i
张印章后,已经凑齐了其中的 1
种的概率。也就是说,这个人很倒霉,买了 i
张结果全是某一种印章(至于具体是哪一种,不管。反正是
N
N
N 种中的一种)。显然:
d
p
[
i
]
[
j
]
=
p
i
×
N
=
p
i
×
1
p
=
p
i
−
1
,
此
时
j
=
1
dp[i][j]=p^i \times N=p^i \times \frac{1}{p}=p^{i-1},此时j=1
dp[i][j]=pi×N=pi×p1=pi−1,此时j=1
[3] 当 i ≥ j 且 j > 1
的时候,即是说,买了第 i
张印章后,已经凑齐了其中的 j
种的概率。这里要又分为两种情况。
<1> 一种是:买的这第 i
张印章在前面 i-1
次购买中未买到过,那么这次购买就是 再在未买到的 n-(j-1)
种中随便选一种购买。(注,这里为什么是 j-1
可能要多花时间理解一下)则:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
×
[
n
−
(
j
−
1
)
]
×
p
dp[i][j]=dp[i-1][j-1]\times[n-(j-1)]\times p
dp[i][j]=dp[i−1][j−1]×[n−(j−1)]×p
<2> 另一种是:买的这第 i
张印章在前面 i-1
次购买中已买到过了,那么这次购买就是 再在已经买到的 j
种中随便选一种购买。(注,这里为什么是 j
也可能要多花时间理解一下)则:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
×
j
×
p
dp[i][j]=dp[i-1][j]\times j \times p
dp[i][j]=dp[i−1][j]×j×p
然后把这两种情况加起来即是,当 i ≥ j 且 j > 1
的时候的 “状态转移方程”:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
×
(
n
−
j
+
1
)
×
p
+
d
p
[
i
−
1
]
[
j
]
×
j
×
p
dp[i][j]=dp[i-1][j-1]\times (n-j+1) \times p+dp[i-1][j]\times j \times p
dp[i][j]=dp[i−1][j−1]×(n−j+1)×p+dp[i−1][j]×j×p
三、完整代码[C++]
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int N, M;
double dp[25][25];
int main()
{
cin >> N >> M;
double p = 1.0 / N;
for (int i = 1; i <= M; i++)
{
for (int j = 1; j <= N; j++)
{
if (i < j)
{
dp[i][j] = 0;
}
else if (j == 1)
{
dp[i][j] = pow(p, i - 1);
}
else if (j > 1)
{
dp[i][j] = dp[i - 1][j - 1] * (N - j + 1) * p + dp[i - 1][j] * j * p;
}
}
}
printf("%.4f", dp[M][N]);
return 0;
}
四、做题小结与感慨
● 动态规划的题,难就难在找 “状态” 和 “状态转移方程”。
● 总结一点关于 dp 的题的一点规律:一般都是从简单地推导开始。就像上面的解析一样,先考虑 i<j
的情况,然后再在 i≥j
的基础上找 j=1
的情况,最后才考虑 j>1
的情况。
五、参考附录
[1] 原题地址:印章.
C/C++百题打卡[11/100]——印章[蓝桥杯 算法训练]
标签:动态规划
不定期更新
2022/3/30