Codeforces 28C Bath Queue
简要题意:有 n 个人等概率随机进入 m 个房间,一个房间可以有多个人,第 i 个房间有 ai 个水龙头,在一个房间的人要去排队装水,他们会使得最长的队尽可能小,求所有房间中最长队列长度的期望
Mark一个很好的blog
然后快考虑这题咋做
你先看n的范围感觉是 n5 n 5 的DP
因为对于每个人,进入每个房间的概率是相同的,所以我们只用每种情况的权值*每种情况的出现概率就可以了,然后又因为每种情况的权值只是最多人数,所以我们只需要统计最多人数的不同情况的方案数就可以了
首先考虑一下每次所有人进到房间之后的状态,一定有一个或多个房间是存在最大值的,然后我们考虑枚举最大值,又因为人进入的顺序没有关系,直接考虑总人数和最后结果的影响就行了
然后我们记 dpi,j,k d p i , j , k 表示前i个房间进入了j个人,最多一个房间排队人数最长是k的方案数
然后我们直接按顺序枚举房间,这里有一个小细节,就是枚举房间的顺序无关紧要,因为我们每次只需要考虑的是当前的最大值出现在这个房间还是其他房间。
然后枚举人数,然后分两种情况
1:最大值出现在这个房间
2:最大值不出现在这个房间
进行讨论,在每个讨论里边枚举一下这个房间进来多少人就好了
感觉应该不难理解了
#include<bits/stdc++.h>
using namespace std;
#define N 110
#define fu(a,b,c) for(int a=b;a<=c;++a)
#define fd(a,b,c) for(int a=b;a>=c;--a)
double dp[N][N][N]={0};
double c[N][N]={0};
int a[N],n,m;
int main(){
scanf("%d%d",&n,&m);
fu(i,1,m)scanf("%d",&a[i]);
fu(i,0,n+m)c[i][0]=1;
fu(i,1,n+m)
fu(j,1,i)
c[i][j]=c[i-1][j-1]+c[i-1][j];
fu(i,0,m)dp[i][0][0]=1;
fu(i,1,m)
fu(j,1,n)
fu(k,0,j){
//case1: 当前的房间到达上线
int l=max(a[i]*(k-1)+1,0),r=a[i]*k;
fu(w,0,k)
fu(p,l,min(r,j))
dp[i][j][k]+=dp[i-1][j-p][w]*c[n-j+p][p];
//case2: 前面某个房间到达上限
fu(p,0,min(l-1,j))
dp[i][j][k]+=dp[i-1][j-p][k]*c[n-j+p][p];
}
double ans=0;
fu(i,1,n)ans+=dp[m][n][i]*i;
fu(i,1,n)ans/=(double)m;
printf("%.10lf",ans);
return 0;
}