看到所有题解都是倒退,却没有一个讲正推的,这里就来讲一下
设 f i f_i fi 表示已经掷到过 i i i 个不同面的时候需要的期望次数
初始化 f 0 = 0 f_0=0 f0=0
那么,转移方程显然是 f i = n − i + 1 n ⋅ f i − 1 + n − i n ⋅ f i + 1 f_i = \frac{n-i+1}{n} \cdot f_{i-1}+\frac{n-i}{n} \cdot f_i +1 fi=nn−i+1⋅fi−1+nn−i⋅fi+1
化简后得到 f i = f i − 1 × ( n − i + 1 ) + n n − i f_i=\frac{f_{i-1}\times(n-i+1)+n}{n-i} fi=n−ifi−1×(n−i+1)+n
可以发现,如果这样递推到 n n n 时,分母就为 0 0 0 了,所以此方法不可行
那么,我们尝试用倒推
设 f i f_i fi 表示已经掷到过 i i i 个不同的面后,期望还需要掷多少次才能到 n n n 个面。初始化 f n = 0 f_n=0 fn=0
转移方程 f i = n − i n ⋅ f i + 1 + i n ⋅ f i + 1 f_i=\frac{n-i}{n} \cdot f_{i+1}+\frac{i}{n} \cdot f_i+1 fi=nn−i⋅fi+1+ni⋅fi+1
化简后得 f i = f − i + 1 + n n − i f_i=f-{i+1}+\frac{n}{n-i} fi=f−i+1+n−in
这样,就不会出现分母为 0 0 0 的情况了,答案为 f 0 f_0 f0
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=1010;
double f[Maxn];
int n,T;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
f[n]=0.0;
for(int i=n-1;i>=0;--i)
f[i]=f[i+1]+(n*1.0)/((n-i)*1.0);
printf("%.2lf\n",f[0]);
}
return 0;
}