AT_dp_j Sushi
前置知识
什么是数学期望?
类似于加权平均,离散型随机变量的一切可能的取值 $x_i$ 与对应的概率 $p(x_i)$ 乘积之和称为该离散型随机变量的数学期望 $E(x)$。
因此,$E(x)$ 可表示为:$$E(x)=\sum^n_{i=1}x_ip(x_i)$$
分析
本题最大的特点是每个盘子中至多有 $3$ 个寿司。
如果有两个盘子都有 $x$ 个寿司,那么它们就没有区别。
也就是说,无论这两个盘子排在哪里,最终输出的答案是一致的。无端联想排列组合。
因此,我们只需要关心不同寿司个数的盘子的数量,这些盘子的顺序对最终答案没有任何影响。
实现
$dp_{i,j,k}$ 表示当当前寿司数为 $1$ 的盘子有 $i$ 个,寿司数为 $2$ 的盘子有 $j$ 个,寿司数为 $3$ 的步数为 $k$ 个时,吃完全部所需的期望。
dp 方程如下:$$dp_{i,j,k}=\frac{n}{i+j+k}+dp_{i-1,j,k}\times\frac{i}{i+j+k}+dp_{i+1,j-1,k}\times\frac{j}{i+j+k}+dp_{i,j+1,k-1}\times\frac{k}{i+j+k}$$
显然,维度 $k$ 具有单调性,因此 $k$ 需要放在循环外层。
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=310;
int n,a[5];
double dp[MAXN][MAXN][MAXN];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int tmp;
cin>>tmp;
a[tmp]++;
}
for(int k=0;k<=n;k++)
{
for(int j=0;j<=n;j++)
{
for(int i=0;i<=n;i++)
{
double s=i+j+k;
if(s!=0)//防止 i,j,k 同时为零
{
if(i)dp[i][j][k]+=dp[i-1][j][k]*i/s;
if(j)dp[i][j][k]+=dp[i+1][j-1][k]*j/s;
if(k)dp[i][j][k]+=dp[i][j+1][k-1]*k/s;
dp[i][j][k]+=(double)n/s;
}
}
}
}
printf("%.10lf\n",dp[a[1]][a[2]][a[3]]);
return 0;
}