Codeforces 261 B Maxim and Restaurant

問題

n人の人がいて、それぞれの人の大きさはa[i]である。
n人はn!通りの並び方を等しい確率で選んで並ぶ。
大きさの和がp以下であるような部分だけ、列の先頭から人がレストランに入れる。
入る人が、入れない人を飛ばすことはできない。


レストランに入れる人数の期待値を求めよ。

制約条件

n≦50
p≦50

方針

最初にレストランに入れなくなる人を決めうち。


入れない人をi番目の人とすると、入れる人の大きさの和はp - a[i]〜a[i]なので、
i番目の人以外でDPをして、
dp[入れる人数][大きさ和] := 場合の数
を求める。
これで、p - a[i]〜a[i]の和に対して、入れる人数 * (入れる人数)! * (入れない人数)! * 場合の数 / n!
を期待値に足せばいい。

ソースコード

int n, a[50], p;
double dp[51][2501], f[100];
double calc(int e){
	memset(dp, 0, sizeof(dp));
	dp[0][0] = 1;
	
	rep(i, n) if(i != e) for(int j = n - 1; j >= 0; j--)
	for(int k = 2500; k >= 0; k--) if(dp[j][k]){
		dp[j + 1][k + a[i]] += dp[j][k];
	}
	double res = 0;
	rep(i, n) for(int j = max(0, p - a[e] + 1); j <= p; j++){
		if(!dp[i][j]) continue;
		//cerr<<"i: "<<i<<" j: "<<j<<" dp: "<<dp[i][j]<<endl;
		res += i * dp[i][j] * f[i] * f[n - 1 - i];
	}
	//cerr<<"e: "<<e<<" res: "<<res<<endl;
	return res;
}
int main(){
	f[0] = 1;
	rep(i, 99) f[i + 1] = f[i] * (i + 1);
	
	cin >> n;
	rep(i, n) cin >> a[i];
	cin >> p;
	
	int s = 0;
	rep(i, n) s += a[i];
	if(s <= p){
		cout << n << endl;
		return 0;
	}
	
	double res = 0;
	rep(i, n) res += calc(i);
	printf("%.9f\n", res / f[n]);
	
	return 0;
}