POJ 2096 (概率DP)

题目链接http://poj.org/problem?id=2096

题目大意:n种bug,s个子系统。每天随机找一个bug,种类随机,来自系统随机。问找齐n种bug,且每个子系统至少有一个bug的期望天数。

解题思路

- -。题目像一坨屎。

其中"且每个子系统至少有一个bug"比较坑爹,其实意思就是找出s个bug就行了。

dp[i][j]表示已找到i种bug,且j个系统有bug的期望。

它可以由四个状态推到:

①dp[i][j], 当前找的bug,种类重复,且系统重复。概率为(i/n)*(j/s)。

②dp[i][j-1],当前找的bug,种类重复,且系统不重复。概率(i/n)*(s-j)/s。

③dp[i-1][j],当前找的bug,种类不重复,且系统重复。概率(n-i)/n*(j/s)。

④dp[i-1][j-1],当前找的bug,种类不重复,且系统不重复。概率为(n-i)/n*(s-j)/s。

最后dp[i][j]+=1.

由于是求期望,所以要逆推,dp[n][s]=0, ans=dp[0][0] 。

dp方程的减号全部改为加号。累加然后你会WA掉。因为double精度丢失太严重了。

dp[i][j]*(i/n)*(j/s)。几次乘几次除,精度会爆。

所以有必要进行化简, 把乘法化在一起,除法化在一起,最后做一步除法。

dp[i,j] = ( 1 + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] )/( 1-p1 )    
          = ( n*s + (n-i)*j*dp[i+1,j] + i*(s-j)*dp[i,j+1] + (n-i)*(s-j)*dp[i+1,j+1] )/( n*s - i*j )

由于POJ数据略水,其实化简到第一步就能A。

#include "cstdio"

#include "cstring"

double dp[1005][1005];

int main()

{

    int n,s;

    while(scanf("%d%d",&n,&s)!=EOF)

    {

        memset(dp,0,sizeof(dp));

        for(int i=n;i>=0;i--)

        {

            for(int j=s;j>=0;j--)

            {

                if(i==n&&j==s) continue;

                double p2=(double(s-j)*i)/n/s;

                double p3=(double(n-i)*j)/n/s;

                double p4=(double(n-i)*(s-j))/n/s;

                double p1=1.0-(double(i*j))/n/s;

                dp[i][j]=p2*dp[i][j+1]+p3*dp[i+1][j]+p4*dp[i+1][j+1]+1;

                dp[i][j]/=p1;

            }

        }

        printf("%.4lf\n",dp[0][0]);

    }

}

 

13634810 neopenx 2096 Accepted 8056K 719MS C++ 679B 2014-11-16 16:08:41

 

 

你可能感兴趣的:(poj)