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

 

 

posted @ 2014-11-16 16:11  Physcal  阅读(527)  评论(0编辑  收藏  举报