Label
期望DP经典例题
Description
一个n面的骰子,求期望掷几次能使得每一面(每一种点数)都被掷到。
数据范围:n≤1000n\leq 1000n≤1000,共t(t≤1000)t(t\leq 1000)t(t≤1000)组数据。
Solution
一种正确但不可行的想法
一开始想着设dp[i][j]dp[i][j]dp[i][j]为投掷了iii次骰子后扔到过jjj个不同面所经过次数的期望,那么这个转移式还是比较好想的:
dp[i][j]=n−(j−1)ndp[i−1][j−1]+jndp[i−1][j](dp[1][1]=1)dp[i][j]=\frac{n-(j-1)}{n}dp[i-1][j-1]+\frac{j}{n}dp[i-1][j](dp[1][1]=1)dp[i][j]=nn−(j−1)dp[i−1][j−1]+njdp[i−1][j](dp[1][1]=1)
ans=∑i=n∞dp[i][n]ans=\sum_{i=n}^{\infty}dp[i][n]ans=∑i=n∞dp[i][n]
dp[i][j]dp[i][j]dp[i][j]由两种情况转移而来:1、dp[i−1][j]dp[i-1][j]dp[i−1][j]:扔了一次骰子还是已出现过的jjj面中的一种,p=jnp=\frac{j}{n}p=nj;2、dp[i−1][j−1]dp[i-1][j-1]dp[i−1][j−1]:扔了一次骰子不是已出现过的j−1j-1j−1面中的一种,p=n−(j−1)np=\frac{n-(j-1)}{n}p=nn−(j−1)。
但是,这种正向推导的方法有一种显然的问题:理论上存在扔无限次骰子而始终无法使得每一面都被掷到的可能性,故此答案没法统计(ansansans收敛于何处实在难求),故我们考虑不依赖于步数的新状态。
正推?
设dp[i]dp[i]dp[i]表示恰好投出iii种不同点数的期望投掷次数,初始状态为dp[1]=1dp[1]=1dp[1]=1,问题来了:dp[i]dp[i]dp[i]由何转移得到?应由dp[i−1]dp[i-1]dp[i−1]和dp[i]dp[i]dp[i]。到了这里,dp[i−1]dp[i-1]dp[i−1]到dp[i]dp[i]dp[i]的概率为n−i+1n\frac{n-i+1}{n}nn−i+1,dp[i]dp[i]dp[i]到dp[i]dp[i]dp[i]的概率为in\frac{i}{n}ni,概率和不为1,不可行。
逆推
设dp[i]dp[i]dp[i]表示当前已投出过iii种不同点数后投掷的期望次数,那么考虑在f[i]f[i]f[i]状态下投掷一次骰子,显然有in\frac{i}{n}ni的概率又扔到原有的iii面骰子,f[i]f[i]f[i]转移到f[i]f[i]f[i],有n−in\frac{n-i}{n}nn−i的概率扔到原来没扔到的n−in-in−i面骰子,f[i]f[i]f[i]转移到f[i+1]f[i+1]f[i+1],所以:
dp[i]=in(dp[i]+1)+n−in(dp[i+1]+1)dp[i]=\frac{i}{n}(dp[i]+1)+\frac{n-i}{n}(dp[i+1]+1)dp[i]=ni(dp[i]+1)+nn−i(dp[i+1]+1)
其中,dp[n]=0dp[n]=0dp[n]=0
化为标准递归公式的形式,即为:dp[i]=dp[i+1]+nn−idp[i]=dp[i+1]+\frac{n}{n-i}dp[i]=dp[i+1]+n−in
(别忘了转移的时候我们扔了一次骰子!)
Code
#include<cstdio>
#include<iostream>
#define ri register int
using namespace std;
const int MAXN=1020;
int t,n;
double dp[MAXN],ans;
int main()
{
scanf("%d",&t);
for(ri opt=1;opt<=t;opt++)
{
scanf("%d",&n);
dp[n]=0;
for(ri i=n-1;i>=0;i--)
dp[i]=dp[i+1]+(double)n/(double)(n-i);
printf("%.2f\n",dp[0]);
}
return 0;
}