现在的位置: 首页 > 综合 > 正文

hdu 4465 Candy( 概率 log 组合数 )

2013年11月21日 ⁄ 综合 ⁄ 共 1357字 ⁄ 字号 评论关闭

     

      题意:两个瓶子里都装了n个糖果;从第一个瓶子拿的概率是p  , 当你再拿糖果的时候,发现瓶子空了 ,求这时候另外一个瓶子的剩余的糖果的数量的期望   (1 ≤ n ≤ 2 × 10^5)

这题的概率DP公式好求,即

       

但c(n,m) 太大,会上溢出, p^k 太小,会下溢出,这时就要用到 log  求组合了


对于问题一:

  排列过大,考虑到 y = logx 函数, log(n!) 也不会很大. 又

  令 f(x) = log( x! ),则有 

  预处理出 函数f() 就可以 O(1)求出 log( \binom{n}{m} )了. 然后 再求个 exp( x ) 就可以还原了.

        exp(x )    返回以 e 为底数、以给定数字为指数的幂。

对于问题二:

  p^x 值过小. log( p^x ) = x * log p 这样值就在可控范围内. 同样一次 exp( x ) 就可以还原


代码如下:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;

#define N 400005
double f[N];

double C(int n,int m)
{
    return f[n]-f[m]-f[n-m]; //c(n,m)=n!/((n-m)!*m!)  log(c(n,m))=log(n!)-log(m!)-log((m-n)!)  
}

int main()
{
    int i,n,j,t=0;

    f[0]=0;
    for(i=1;i<N;i++)
      f[i]=f[i-1]+log(i*1.0);

    double p,q,res;
    while(~scanf("%d%lf",&n,&p))
    {
        t++;
        q=1.0-p;
        res=0;
        for(i=0;i<n;i++)
        {
           res=res+1.0*(n-i)*(exp( C(n+i,i)+i*log(p)+(n+1)*log(q))+exp(C(n+i,i)+i*log(q)+(n+1)*log(p)));
        }
        printf("Case %d: %.6lf\n",t,res);
    }
    return 0;
}

还有一种方法是直接求的,没用log

是边求组合数一边求概率

#include<iostream>
#include<cstdio>
#include<string.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;

int main()
{
    int i,n,j,t=0;
    double p,q,res,ca,cb;
    while(~scanf("%d%lf",&n,&p))
    {
        t++;
        q=1.0-p;
        ca=cb=1.0;
        int exp=n+1;
        res=n*(pow(q,exp)+pow(p,exp));
        for(i=1; i<n; i++)
        {
            ca=ca*(n+i)*p/i;
            cb=cb*(n+i)*q/i;
            while(ca>n||cb>n)
            {
                ca=ca*q;
                cb=cb*p;
                exp--;
            }
            res+=ca*(n-i)*pow(q,exp);
            res+=cb*(n-i)*pow(p,exp);
        }
        printf("Case %d: %.6lf\n",t,res);

    }
    return 0;
}

        

抱歉!评论已关闭.