POJ 2096Collecting Bugs ---期望DP(卡精度)

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

题目大意:一个软件有s个子系统,会产生n种bug。某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中
求找到所有的n种bug,且每个子系统都找到bug,这样所要的天数的期望。需要注意的是:bug的数量是无穷大的,所以发现一个bug,出现在某个子系统的概率是1/s,属于某种类型的概率是1/n。

Sample Input

1 2

Sample Output

3.0000

emm,期望DP,有2种变量,那么一般是二维DP,设dp[i][j],关键是dp数组的表示,它表示成什么,如果有了一个合理的解释,那么困难就会很好地解答题目,这里可以解释为已经找到$i$种问题,并且存在$j$个子系统中,也可以解释为剩余量。

那么这样转移的状态就很明显了,$dp[i][j]$由$dp[i-1][j],dp[i][j-1],dp[i-1][j-1],dp[i][j]$转移过来,只是正着推。。。我推不出来,都有点问题,那么就只能反向尝试了,$dp[i][j]$由$dp[i+1][j],dp[i][j+1],dp[i+1][j+1],dp[i][j]$转移过来。他们发生转移的概率为$frac{n-i}{n} imes frac{j}{s}$

$frac{i}{n} imes frac{s-j}{s}$

$frac{n-i}{n} imes frac{s-j}{s}$

$frac{i}{n} imes frac{j}{s}$

这个可以理解为由dp[i][j]转移到四个状态的概率,转移到dp[i+1][j]即产生新的问题,概率为$frac{n-i}{n}$,但还是在原来的子系统中,概率为$frac{j}{s}$。

那么答案出来了,我们将上述概率记为p1,p2,p3,p4,那么$dp[i][j]=p1*dp[i+1][j]+p2*dp[i][j+1]+p3*dp[i+1][j+1]+p4*dp[i][j]+1$。这个+1是每次的转移需要一天的时间锁产生的。那么也就是$(1-p4)*dp[i][j]=p1*dp[i+1][j]+p2*dp[i][j+1]+p3*dp[i+1][j+1]+1$。最后把$1-p4$除过去就OK了,但非常坑的是,p1,p2,p3,p4不能直接这么算,由于每次的除法都会损失一定的精度,所以这样写会使得答案的精度不够导致WA,WA,WA。。。我们就先做乘法,最后再除。

以下是AC代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mac=1e3+10;

double dp[mac][mac];

int main() {
    int n,s;
    while (~scanf ("%d%d",&n,&s)) {
        dp[n][s]=0;
        for (int i=n; i>=0; i--)
            for (int j=s; j>=0; j--) {
                if (i==n && j==s) continue;
                double p1=1.0*(n-i)*j,p2=1.0*i*(s-j),p3=1.0*(n-i)*(s-j),p4=1.0*i*j;
                dp[i][j]=(dp[i+1][j]*p1+dp[i][j+1]*p2+dp[i+1][j+1]*p3+n*s)/(n*s)/(1-p4/(n*s));
            }
        printf("%.4f
",dp[0][0]);
    }

    return 0;
}
路漫漫兮
原文地址:https://www.cnblogs.com/lonely-wind-/p/13233156.html