「P4550」 收集邮票

08 / 28 / 2020 | 最后修改于 08 / 28 / 2020

​一眼看上去就是期望 DP,然而蒟蒻不会,在同机房神仙讲解下豁然开朗,于是写篇题解。

Solution

​直接设期望买完的钱数不很好搞,因为每次买邮票所需的钱数是随购买次数变化的,所以我们绕个路

​设 fif_i 表示已经买到 ii 邮票,若要买完剩下的邮票还需买多少,转移如下:

fi=infi+ninfi+1+1f_i = \frac i n f_i + \frac {n-i} n f_{i+1} + 1

​初始状态是 fn=0f_n = 0infi\frac i n f_i 含义是你有 in\frac i n 的概率买到已经买到的 ii 种邮票之一,状态仍然是 fif_ininfi+1\frac {n-i} n f_{i+1} 含义是你有 nin\frac {n-i} n 的概率买到还没买到的 nin - i 种邮票之一,状态是 fi+1f_{i+1} ,最后在加上这次买的一次贡献。

​再设 gig_i 表示已经买到 ii 邮票,若要买完剩下的邮票还需买多少,转移如下:

gi=in(gi+fi+1)+nin(gi+1+fi+1+1)g_i = \frac i n (g_i + f_i + 1) + \frac {n-i} n (g_{i+1} + f_{i+1} + 1)

​初始状态是 gn=0g_n = 0 ,仍然很好想。那 in(gi+fi+1)\frac i n (g_i + f_i + 1) 是什么意思呢?放图镇楼:

​里面加的那个 fi+1f_i + 1 其实就是给后面的每一次购买都+1,因为当你这次购买后,比如后面的某次是原来是第 hh 次,现在就是第 h+1h + 1 次,价格+1,那么后面有几次购买呢?就是 fif_i 次,最后别忘了你这第 ii 次也要+1。 nin(gi+1+fi+1+1)\frac {n-i} n (g_{i+1} + f_{i+1} + 1) 同理。

最后,把 fi,gif_i, g_i 移项化简一下就好了:

fi=fi+1+nnigi=inifi+gi+1+fi+1+nnif_i = f_{i+1} + \frac n {n-i} \\ g_i = \frac i {n-i} f_i + g_{i+1} + f_{i+1} + \frac n {n-i}

Talk is cheap, show me your code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <cstdio>

const int N = 10005;
int n;
double f[N], g[N];

int main() {
#ifndef ONLINE_JUDGE
#ifdef LOCAL
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#endif
#ifndef LOCAL
freopen("CppTest.in", "r", stdin);
freopen("CppTest.out", "w", stdout);
#endif
#endif

scanf("%d", &n);
for (int i = n - 1; ~i; --i) {
f[i] = f[i + 1] + 1.0 * n / (n - i);
}
for (int i = n - 1; ~i; --i) {
g[i] = 1.0 * i / (n - i) * f[i] + g[i + 1] + f[i + 1] + 1.0 * n / (n - i);
}
printf("%.2lf", g[0]);
return 0;
}