这个题向我充分展示了数学的简洁之美。
自己做的时候是从正面做的, 推出了个公式 ∑ (1/2)^(2n-k)*C(2n-k-1, n-k) (2<=k<=n, n是总人数的一半)。 结果这个公式超时不说, 可以打表, 但精度不够, 大概2000+就不行了。 又发现一个规律可以把时间降到o(n), 但是精度损失更大。
还是得看题解, 都是反着求得:已确定最后俩个人的汉堡是不一样的, 那么前面每次都会抛硬币, 概率为(1/2)^(n-2), (n是总人数, 下同), 选(n-2)/2个人拿同一种汉堡, 则剩下的人拿另一种, 组合数为C(n-2, (n-2)/2), 答案就是(1/2)^(n-2)*C(n-2, (n-2)/2)。
这样做估计还是超时, 不过可以打表了。
还可以继续优化, 找出递推公式, 边界f(2)=1, f(n)=f(n-2)*(n-3)/(n-2)。 竟然可以这么简单。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <string>
#include <set>
#include <map>
#include <list>
#include <vector>
#include <queue>
#include <stack>
#include <sstream>
#include <iomanip>
typedef long long ll;
typedef unsigned long long llu;
using namespace std;
int main()
{
double f[100000+10];
f[2]=1.0;
for(int i=4; i<=100000+5; i+=2)
f[i]=f[i-2]*(i-3)/(i-2);
int n, t;
cin>>t;
while(t-- && scanf("%d", &n))
printf("%.4lf\n", 1-f[n]);
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <string>
#include <set>
#include <map>
#include <list>
#include <vector>
#include <queue>
#include <stack>
#include <sstream>
#include <iomanip>
typedef long long ll;
typedef unsigned long long llu;
using namespace std;
double fun(int n, int m)
{
double ans=0.5;
double i=1.0;
for(int j=1.0; j<=m; j++, i++)
ans*=0.5;
for(int j=1.0; j<=n-m; j++, i++)
ans*=i/j*0.5;
return ans;
}
//double fun(int n, int m)
//{
// double ans=0.5;
// for(double i=1.0; i<=m; i++)
// ans=ans*(n-i+1.0)/i*0.5;
// for(int i=0; i<n-m; i++)
// ans*=0.5;
// return ans;
//}
double solve(int n)
{
double ans=0;
for(int k=2; k<=n; k++)
ans+=fun(2*n-k-1, n-k);
return ans;
}
int main()
{
int n, t;
cin>>t;
while(t-- && scanf("%d", &n))
printf("%.4lf\n", 2*solve(n/2));
return 0;
}