传送门
题目分析
我们可以设
f
(
n
,
k
)
f(n,k)
f(n,k)表示共
n
n
n个名字,剩下
k
k
k个名字未收集到,需购买饮料的平均次数。
则有:
f
(
n
,
k
)
=
(
n
−
k
)
f
(
n
,
k
)
n
+
k
f
(
n
,
k
−
1
)
n
+
1
f(n,k)=\frac {(n-k)f(n,k)}{n}+ \frac{kf(n,k-1)}{n}+1
f(n,k)=n(n−k)f(n,k)+nkf(n,k−1)+1经过移项整理,会得到递推式:
f
(
n
,
k
)
=
f
(
n
,
k
−
1
)
+
n
k
f(n,k)=f(n,k-1)+\frac {n}{k}
f(n,k)=f(n,k−1)+kn
根据递推式可得:
f
(
n
,
0
)
=
n
∑
k
=
1
n
1
k
f(n,0)=n\sum_{k=1}^{n}\frac{1}{k}
f(n,0)=nk=1∑nk1
同时注意约分和输出即可
Code
#include <cstdio>
#include <iostream>
#define ll long long
using namespace std;
inline int read()
{
int x = 0, f = 1; char c = getchar();
while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <='9') {x = x * 10 + c - '0'; c = getchar();}
return f * x;
}
int n;
ll p,q=1,g;
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
int wei(ll x)//计算位数
{
int res = 0;
for (; x; x /= 10) ++res;
return res;
}
int main()
{
n = read();
for (int i = 1; i <= n; ++i)
{
p = p * i + q * n; q *= i;//递推
g = gcd(p, q);
p /= g, q /= g;//过程约分
}g = p / q, p %= q;
if (!p) printf("%lld\n", g);
else
{
for (int i = wei(g); i >= 1; --i) putchar(' ');
printf("%lld\n", p);
if(g) printf("%lld",g);
for (int i = wei(q); i >= 1; --i) putchar('-');
printf("\n");
for (int i = wei(g); i >= 1; --i) putchar(' ');
printf("%lld\n",q);
}
return 0;
}
总结与反思
- 当数据较大时,要每一步进行模数、约分等操作,同时注意要先除后乘,防止爆数据结构
- 期望 d p dp dp的题目很有趣,码量并不大,但要熟练掌握期望 d p dp dp方程式的推法