洛谷P4550 收集邮票 题解 期望DP
题目链接:https://www.luogu.com.cn/problem/P4550
解题思路:
定义状态 \(f_i\) 表示目前已经取到 \(i\) 种邮票的情况下,取完所有 \(n\) 种邮票,还需要取的期望次数。
很明显,\(f_n = 0\),因为此时已经取完了 \(n\) 种邮票,不需要再取就能满足条件了。
如果当前已经取到了 \(i\) 种邮票,则下一次取:
- 有 \(\frac{i}{n}\) 的概率取到现有的邮票(此时仍然拥有 \(i\) 种邮票);
- 有 \(1 - \frac{i}{n}\) 的概率取到没有的邮票(此时拥有了 \(i+1\) 种邮票)。
所以,当 \(i \lt n\) 时:
(多出来的 \(1\) 是额外的操作次数)
化简得到:
\(\frac{n-i}{n} f_i = \frac{n-i}{n} f_{i+1} + 1\)
\(\rightarrow\)
当 \(i \lt n\) 时:
定义状态 \(g_i\) 表示目前已经取到 \(i\) 种邮票的情况下,取完所有 \(n\) 种邮票,还需要的期望代价。
"第 \(k\) 次取的花费为 \(k\)"。\(\Rightarrow\) 但是这么分析会很麻烦,我们可以把问题转换为 "倒数第 \(k\) 次取的花费为 \(k\)"。
这两句话其实是等价的:
因为假设取 \(m\) 次,那么:
- 按照 "第 \(k\) 次取的花费为 \(k\)" 总的花费为 \(1 + 2 + 3 + \ldots + m = \sum\limits_{i=1}^m i\);
- 按照 “倒数第 \(k\) 次取的花费为 \(k\)” 总的花费为 \(m + (m-1) + (m-2) + \ldots + 2 + 1 = \sum\limits_{i=1}^m i\)$。
但是按照这样分析,接下来解决起来会好理解很多。这也是我在看 洛谷上的题解 的时候(我感觉)前几篇题解没有讲的很清楚的地方 后几篇的题解我没看
而取到 \(i\) 种不同的邮票的期望次数是 \(f_i\) 次,所以在拥有 \(i\) 种不同邮票的情况下,要取完 \(n\) 种邮票的期望次数是 \(f_i\) 次,所以我:
- 此时如果取到的是一张现有的邮票,则我还有 \(f_i\) 次要取,我这次的期望取的次数是倒数第 \(f_i + 1\) 次,花费为 \(f_i + 1\);
- 此时如果取到的是一张新的邮票,则我还有 \(f_{i+1}\) 次要取,我这次的期望取的次数是倒数第 \(f_{i+1} + 1\) 次,花费为 \(f_{i+1} + 1\)
此时我:
- 有 \(\frac{i}{n}\) 的概率取到现有的邮票(此时仍然拥有 \(i\) 种邮票);
- 有 \(1 - \frac{i}{n}\) 的概率取到没有的邮票(此时拥有了 \(i+1\) 种邮票)。
所以,当 \(i \lt n\) 时:
化简得到:
\(\frac{n-i}{n} g_i = \frac{i}{n} (f_i + 1) + \frac{n-i}{n} (g_{i+1} + f_{i+1} + 1)\)
\(\Rightarrow\)
最终的答案为 \(g_0\)。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
double f[maxn], g[maxn];
int n;
int main() {
cin >> n;
for (int i = n-1; i >= 0; i--)
f[i] = f[i+1] + 1. * n / (n - i),
g[i] = g[i+1] + f[i+1] + 1 + 1. * i / (n - i) * (f[i] + 1);
printf("%.2lf\n", g[0]);
return 0;
}