ZJU2949 Coins of Luck - 数学期望

题目描述:

现在有A、B面条各有N碗,用投掷硬币的方式来决定吃哪种面条。当只剩下一种面条时不需要再投掷硬币。输入N,要求将所有面条吃完投掷硬币的数学期望。(N<=1000)

分析:

这个短语一定要认识:mathematical expectation(数学期望)。

回忆求数学期望的一般方法,E=∑ pi×xi。即所有可能的取值×取值的概率然后求和。

我们只考虑把A面条吃完,剩下若干B面条的情况,得出的结果乘以2即可。记组合数c(i,n)为c[n][i];记2的n次幂为pow2[n]。

那么吃完n碗A面条,吃了i碗B面条(0<=i<n)时的概率为:c[n-i-1][i] / pow2[n+i];这种情况下投掷硬币的次数为n+i;

那么总的期望公式为:E =  2 × ∑( ( c[n-i-1][i] / pow2[n+i] ) × ( n+i ) ) , (0<=i<n)

可以这样理解:总共吃了n+i碗面条,一共要投掷n+i次硬币,所以一共有pow2[n+i]种可能性。而满足吃完n碗A,吃了i碗B的条件,必须最后一碗是吃A面条(因为结束投掷硬币是在A刚好吃完的时候),所以满足条件的情况总数为,前n-1个A和i个B的全排列,也就是n-i-1个位置选i个放置B,其余放置A,共c[n-i-1][i]种情况。

那么期望值为,所有情况的投掷硬币次数×概率之和,再乘以2。

到这里,可以说题目刚刚完成了一半。由于n的范围是1000,组合数c[n][i]和2的n次幂pow2[n]都会很大。是不是需要用大数呢?

分析最后的输出结果只要求保留两位小数,答案也不会超过2000。也就是说,我们尝试用double直接算,只要保证运算过程中不超出范围,最后输出的精度一定是足够的。

查了一下double的取值范围,大约是±1e308,大约是2的1024次方。

单看pow2[n+i],n+i的取值是2000,这就已经超范围了。仔细观察,其实公式可以变形,将pow2[n]提到公式外面:

E = ( 2 / pow2[n] ) × ∑( ( c[n-i-1][i] / pow2[i] ) × ( n+i ) ) , (0<=i<n)

这样,公式外面和里面的pow2[]数组都在double范围内了。但是尝试了一下用杨辉三角的方式计算c[n][i],到后面的时候还是超了……

和SHP折腾了半天,这里还可以优化一下。观察公式,由于c[x][i]对应的项一定会除以pow2[i],那么就可以在计算c[x][i]的时候边算边除。也许就不会超范围了。

杨辉三角公式,c[n][i] = c[n-1][i] + c[n-1][i-1]。

现在新的c[n][i]保存的是c[n][i] / pow2[i],变形公式:c[n][i] = c[n-1][i] + c[n-1][i-1] / 2。

为什么只在c[n-1][i-1]这项除2?因为按照新的定义方式,c[n-1][i]已经除过pow2[i],c[n-1][i-1]已经除过pow2[i-1],要使c[n][i]的分母是pow2[i],只要在c[n-1][i-1]这一项除2即可。

到这里,题目基本上完美解决了(实际推导过程花了不少时间)……

现在用新定义的c[n][i]来描述最后的公式为:

E = ( 2 / pow2[n] ) × ∑( c[n-i-1][i] × ( n+i ) ) , (0<=i<n)

花絮:

这是周六和SHP在励剑做一套的ZJU校赛题目,主要为了准备珠海赛热身。

前三个水题大约在2小时的时候做掉了,剩下大约3小时的时间一直在推这个题,百般波折,公式还推错了几次……

SHP最初的做法没有事先计算c[n][i]而是实时计算,时间复杂度增加到了O(n^3),而实际只需要O(n^2),虽然答案正确,但还是没摆脱TLE的命运……

我按照我的方式改写了一遍,一遍改写一遍和SHP一起修正公式,在快到5小时时候终于AC掉了~一阵狂喜……

粗略看了一下排名,200人的比赛我们总共4题,应该是在40名左右,可以拿银奖了。很高兴,去鸿兴源大吃一顿……

虽然程序AC了,但是看现场比赛的情况,也有好几个队伍是很快就过掉了,不知是有更优秀的公式呢?还是ZJU的大牛们推这种公式就是家常便饭……迷茫ing,期待更好的解法。。

---------------------------------------------------------------------------------------------------------

#include <stdio.h>
#define N 1005

double c[N+N][N+N];
double pow2[N];
double ans[N];

int main()
{
    int i,j,k,m,n,T;
   
    //pow2[]
    pow2[0]=1;
    for(i=1;i<N;i++){
        pow2[i]=pow2[i-1]*2.0;
    }
   
    //c[][]
    for(i=0;i<N;i++){
        c[i][0]=1;
        c[i][i+1]=0;
    }
    for(i=1;i<N+N;i++){
        for(j=1;j<=i;j++){
            c[i][j]=c[i-1][j-1]/2+c[i-1][j];
        }
    }
   
   
    //ans[]
    for(n=1;n<=1000;n++){
        ans[n]=0;
        for(i=0;i<n;i++){
            ans[n]+=(c[n+i-1][i])*(n+i);
        }
        ans[n]/=pow2[n-1];
    }
   
   
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        printf("%.2lf/n",ans[n]);
    }
   
    return 0;
}

你可能感兴趣的:(ZJU2949 Coins of Luck - 数学期望)