POJ 1322 Chocolate(概率DP)

Description
有c种颜色的巧克力,每种都有无穷个,现在要等概率的取n个巧克力放在桌子上,如果有桌上有两个巧克力颜色一样就都吃掉,问最后桌子上剩m个巧克力的概率
Input
多组用例,每组用例为三个整数c,n(c<=100,n,m<=1000000),以0结束输入
Output
对于每组用例,输出拿出n块巧克力后桌子上剩m块巧克力的概率
Sample Input
5 100 2
0
Sample Output
0.625
Solution
概率题,用dp[i][j]表示拿了i块巧克力后剩j块的概率,那么很容易写出转移方程dp[i][j]=dp[i-1][j-1] * (c-j+1) / c+dp[i-1][j+1] * (j+1) / c,从方程可以看出,第一维可以用滚动数组节省空间,但这样做的时间复杂度是O(10^8),所以还要优化,经过分析可以看出当i+j为奇数时,dp[i][j]=0,这是因为每次会吃掉两块相同颜色的巧克力,所以i-j必然为偶数,这样可以降低一半的复杂度,但是还是会超时,所以在输出上再优化,由于只需要保留三位小数,所以当n>1000的时候,只需要考虑前1000或1001(取决于n的奇偶)个巧克力,之后拿出巧克力对结果的贡献太小在输出中会被忽略所以可以不考虑,那么时间复杂度就是O(5*10^4)
Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 111
double dp[2][maxn];
int c,n,m;
int main()
{
    while(scanf("%d%d%d",&c,&n,&m),c)
    {
        memset(dp,0,sizeof(dp));
        if(m>n||m>c||(m+n)%2)
        {
            printf("0.000\n");
            continue;
        }
        if(n>1000)n=1000+n%2;
        dp[0][0]=1.0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=i&&j<=c;j++)
            {
                dp[i%2][j]=0;
                if((i+j)%2)continue;
                if(j>0)dp[i%2][j]+=dp[1-i%2][j-1]*(c-j+1)/c;
                if(j+1<=i-1)dp[i%2][j]+=dp[1-i%2][j+1]*(j+1)/c;
            }
        printf("%.3lf\n",dp[n%2][m]);
    }
    return 0;
}

你可能感兴趣的:(POJ 1322 Chocolate(概率DP))