[CF28C] Bath Queue

前言

经小提示做出来的DP,因为题解没看懂,淦

题目

洛谷

CF

题目大意:

​ 有\(n(1≤n≤50)\)个学生同时洗衣服,有\(m(1≤m≤50)\)个洗衣房,第\(i\)个洗衣房里有\(a_i(1≤a_i≤50)\)台洗衣机。
​ 每个学生会从\(m\)个洗衣房里随机选择一个,选择同一个洗衣房的学生会按照房间里洗衣机的数量平均排队,求最长队列人数的期望,输出误差不大于\(10^{-9}\)

​ 平均排队是指不存在两个队列人数相差大于二。

讲解

既然我期望学的差,就不要试图直接DP转移期望了(所以没看懂题解qwq)

所以我们DP求方案数

我们令\(dp[i][j][k]\)为前\(i\)个房间去\(j\)个人,最长队列恰好\(k\)的方案数,转移为当前房间进入\(l\)个人,由\(i-1\)转移而来

考虑状态转移方程怎么写

第一维:\(i-1\)\(i\)

第二维:\(j\)\(j+l\)

第三维:\(k\)\(max(⌈\frac{l}{a_i}⌉,k)\)

那么\(dp[i][j][k]\)提供的贡献是多少呢?因为人是不一样的,所以我们要从剩下的\(n-j\)人中选出\(l\)个人来,所以要乘上\(C_{n-j}^{l}\)

所以总的状态转移方程即为:

\[dp[i][j+l][max(ceil(l/a[i]),k)]=\sum dp[i-1][j][k]*C_{n-j}^{l} \]

由于多个\(\sum\)会使得转移方程看起来过于冗长且没有什么太大意义,所以我省略掉了

所以总方案数为:\(T=\sum_{i=0}^n dp[m][n][i]\)

期望与带权平均差不多,而每个\(dp[m][n][i]\)的权值即为\(i\)

所以答案为:

\[\sum_{i=1}^{n}\frac{i*dp[m][n][i]}{T} \]

什么?你问我为什么\(i\)\(1\)开始?难道和从\(0\)开始有什么区别吗?

注意:

1.输入的\(n,m\)分别指人和房间数,不要搞反了

2.输出位数不要太少了

3.建议用\(\texttt{double}\)存,万一用\(\texttt{long long}\)出事了呢

代码

int n,m,a[MAXN];
double C[MAXN][MAXN],dp[MAXN][MAXN][MAXN],ans,T;
void pre()
{
	C[0][0] = 1;
	for(int i = 1;i <= 50;++ i)
	{
		C[i][0] = 1;
		for(int j = 1;j <= i;++ j) C[i][j] = C[i-1][j-1] + C[i-1][j];
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	pre();
	n = Read(); m = Read();
	for(int i = 1;i <= m;++ i) a[i] = Read();
	dp[0][0][0] = 1;
	for(int i = 1;i <= m;++ i)
		for(int j = 0;j <= n;++ j)
			for(int k = 0;k <= j;++ k)
				for(int l = 0;l <= n-j;++ l)
					dp[i][j+l][Max(((l+a[i]-1)/a[i]),k)] += dp[i-1][j][k]*C[n-j][l];
	for(int i = 0;i <= n;++ i) T += dp[m][n][i];
	for(int i = 1;i <= n;++ i) ans += i * dp[m][n][i] / T;
	printf("%.10f",ans);
	return 0;
}
posted @ 2020-10-04 15:08  皮皮刘  阅读(147)  评论(0编辑  收藏  举报