1. 程式人生 > >【區間DP+期望】Gym101623E English Restaurant

【區間DP+期望】Gym101623E English Restaurant

【題目】
原題地址
一個餐廳有 n n 張桌子,第 i i 張座位數為 c i

c_i 。先後有 t t 群人來吃飯,人數為 [ 1 , g
] [1,g]
中隨機一個數。他們會找一張恰好能坐下的桌子坐下(找不到就離開)。求最終期望有多少個人吃飯。 n , t 100 ,
g , c i 200 n,t\leq 100,g,c_i\leq 200

【解題思路】
首先我們可以先新增 n n 個座位 + +\infty 的桌子表示離開餐館,然後按座位數排序。

考慮用期望的定義來求答案,同時 DP \text{DP} 出總和和方案數。

注意到最後佔據的一定是若干個區間,每個區間可以獨立計算。

我們設 f i , j f_{i,j} 表示只有 [ i , j ] [i,j] 的桌子被佔據的人數總和以及方案數,列舉最後一張坐的桌子是什麼來進行轉移,這裡有一個組合數 ( j i k i ) {j-i\choose k-i} 表示前 j i j-i 張桌子的排列種類數。

然後設 g i , j g_{i,j} 表示 [ i , t ] [i,t] 這些人佔據了 [ j , n ] [j,n] 這些桌子的總和以及方案數,利用字首和優化轉移。

(或者我們可以設接下來設 g i , j g_{i,j} 表示前 i i 張桌子佔了 j j 張的期望。列舉 k k 表示 i k + 1 i i-k+1\sim i 都佔了, i k i-k 不佔,當 k = 0 k=0 表示 i i 不佔,這裡有一個組合數 ( j k ) {j\choose k} 表示前 j j 張桌子順序。)

複雜度 O ( n 3 ) O(n^3)

【參考程式碼】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<db,db> pdd;
const int N=205;
int n,g,t,c[N];
db C[N][N];
pdd w[N][N],f[N][N],s[N][N];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}
pdd operator +(const pdd&a,const pdd&b){return mkp(a.fi*b.se+a.se*b.fi,a.se*b.se);}
pdd operator *(const pdd&a,const pdd&b){return mkp(a.fi+b.fi,a.se+b.se);}

void initC()
{
	for(int i=0;i<N;++i)
	{
		C[i][i]=C[i][0]=1;
		for(int j=1;j<i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
}

pdd calc(int l,int r)
{
	int x=min(g,l>0?c[l-1]:0),y=min(g,c[r]);
	return mkp(c[r]<N?(y*(y+1)-x*(x+1))/2:0,y-x);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("E.in","r",stdin);
	freopen("E.out","w",stdout);
#endif
	initC();n=read();g=read();t=read();
	for(int i=0;i<n;++i) c[i]=read();
	for(int i=0;i<t;++i) c[n++]=N;
	sort(c,c+n);
	for(int i=0;i<n;++i)
	{
		w[i][i]=calc(i,i);
		for(int j=i-1;~j;--j) for(int k=j;k<=i;++k)
		{
			pdd tmp=mkp(0,C[i-j][k-j])+calc(j,k);
			if(j<k) tmp=tmp+w[j][k-1];
			if(i>k) tmp=tmp+w[k+1][i];
			w[j][i]=w[j][i]*tmp;
		}
	}
	for(int i=t-1;~i;--i) for(int j=n-1;~j;--j) f[i][j]=w[j][j+t-i-1];
	for(int i=t-1;~i;--i) for(int j=n-1;~j;--j) 
	{
		for(int k=i+1;k<t;++k) 
			f[i][j]=f[i][j]*(mkp(0,C[t-i][t-k])+w[j][j+k-i-1]+s[k][j+k-i+1]);
		s[i][j]=s[i][j+1]*f[i][j];
	}
	pdd ans=mkp(0,0);
	for(int i=0;i<n;++i) ans=ans*f[0][i];
	printf("%.10lf\n",ans.fi/ans.se);
	return 0;
}