题目链接: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; }