Uva 557 – Burger (log(x)应用 , 组合数学)

来源:互联网 发布:jdk1.7 windows 64 编辑:程序博客网 时间:2024/05/21 15:06

传送门: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=115&page=show_problem&problem=498

题意:Ben Bill生日,邀请朋友去m记。有2n个人(包括Ben Bill)。n个汉堡包和n个乳酪汉堡。从Bill左边的女孩开始分汉堡,一直往左,最后转一圈回到Ben 和 Bill 。分汉堡的方法是用一枚硬币,每个人扔出正面就拿汉堡,反面就拿另一种。求出Ben 和Bill 能拿到同一种汉堡的概率。n<=10W

思路:

只要两种汉堡都剩下至少一个,能拿到其中一种的概率都是0.5。本题可以先求二人刚好分得不同的汉堡(就是前2n-2个人有n-1个拿到一种汉堡,另外n-1个拿到另外一种)的概率,在用1减去。

p = 1 – C (2n-2 , n-1) * 0.5 2n-2   (2n-2重伯努利实验满足二项分布。)

不过2n可以高达10W,对于组合排列数C来说。10W的结果数量级大概是10W ! 的一半。十分庞大,double根本装不下。即使可以使用高精度浮点模板,如此高的精度也使得时间复杂度十分大。而0.5的10W次方又是一个相当小的数字,精度过高。如果用double储存会直接被当做无穷小也就是零了。不过如果二者做乘积,结果数量级抵消。所以现在面临的问题是中间结果太大。

 

这种情况通常可以通过分步运算,保持数据在可控的范围内。 (2n-2 , n-1) = (2n-2)! / ( (n-1) ! *( n-1) ! )这样可以分步,先乘分子的一部分,再除分母的一部分。结果可以保证准确。不过很遗憾,10W次不断的浮点乘除,巨大的时间复杂度还是不能通过数据。

利用log函数可以将数据的数量级大大减少。10的10W次方经过log函数处理,结果也就是10W左右。

结果可以这样写

p = 1 – 10 a

a= log10  C (2n-2 , n-1)  +(2n-2) * log10 0.5

log10(2n-2) ! -log10 (n-1)! -log10(n-1) ! ) + (2n-2)  *  log10 0.5.

可以预先求出1~10W 阶乘对10取对数的结果,这样在程序中可以直接调用!代入能直接得出结果。加上一开始预处理求1~10W 阶乘对10取对数的结果。这是相当大的一个优化了。

#include<iostream>#include<cstdio>#include<limits.h>#include<cmath>#include<cstdlib>#include<cstring>using namespace std;double log_n[100000];const double aa = log(0.5);int main(){    int i,j,t;    double ans ;    log_n[0] = 0;    for(i=1;i<=100000;i++){           // 可以利用之前的结果递推a[i] = log(i!) = log((i-1)!*i) = a[i-1] + log(i);        log_n[i] = log_n[i-1] + log(i) ;    }    scanf("%d",&t);    while(t--){        scanf("%d",&i);        j = i/2;        ans = log_n[i-2] - log_n[j-1] - log_n[j-1];        ans += (i - 2) * aa;        ans = exp(ans);        printf("%.4lf\n",1-ans);    }    return 0;}


原创粉丝点击