poj2096
收集漏洞
·述题意:
输入n,s表示这里存在n种漏洞和s个系统(0<n,s<=1000)。工程师可以花费一天去找出一个漏洞——这个漏洞可以是以前出现过的种类,也可能是未曾出现过的种类,同时,这个漏洞出现在每个系统的概率相同。要求得出找到n种漏洞,并且在每个系统中均发现漏洞的期望天数。
·分析:
这是一道求期望值的题目。题目中的两个关键字提醒我们二维状态设计或许很美妙。根据上题的路子,我们用状态f[i][j]表示已经发现了i种漏洞同时已经有j个系统发现了漏洞的情况下最终达到题目要求(f[n][s])的期望天数。
进一步。由题目可知,其实每次漏洞有两种情况(发现过的漏洞和新的漏洞),同时这个漏洞所在的系统也有两种情况(之前已经发现漏洞的系统和之前没有发现漏洞的系统),所以组合一下,共有四情况,一起来转移吧:
由图,我们可以轻松得到转移方程吗?还差一丢丢。因为目的是求出期望值——什么是期望值?好吧,暂时可以理解为“权值 x 概率”。因此期望Dp的转移是有代价的,而不像概率Dp那样简单统计了。另外一个问题,类似于上文的机器人分身,当前状态的期望值有多个转移方向,所以此处要乘上概率——也就是选择这一步的概率P,如下:
f[i][j]—>f[i+1][j+1]: P1=(n-i)*(s-j)/n*s
f[i][j]—>f[i+1][j] : P2=(n-i)*j /n*s
f[i][j]—>f[i][j+1] : P3=i*(s-j) /n*s
f[i][j]—>f[i][j] : P4=i*j /n*s
然后算上转移的代价(1天),我们开始思考最终的DP转移方程式。这里我们将f[n][s]=0定为边界——很合理,表示找到n种漏洞,有s个系统发现漏洞距离目标状态的期望天数(就是一样的状态,所以期望天数是0啊)。据此我们设计出一个逆推的Dp方程式:
f[i][j]=
(f[i][j]+1)*P4+(f[i][j+1]+1)*P3+(f[i+1][j]+1)*P2+(f[i+1][j+1]+1)*P1
你会发现方程左右两边都有f[i][j],所以就对式子进行化简。化简如下:
f[i][j]=f[i][j]*P4+f[i][j+1]*P3+f[i+1][j]*P2+f[i+1][j+1]*P1+(P1+P2+P3+P4)
f[i][j]*(1-P4) = f[i][j+1]*P3+f[i+1][j]*P2+f[i+1][j+1]*P1 + 1
最终就是将左边系数除过去然后带入p1p2p3p4,逆推转移就是了,答案当然就在f[0][0]诞生啦,代码也来啦:
#include<stdio.h> #define ro(i,a,b) for(int i=a;i>=b;i--) const int N=1003;int n,m;double f[N][N]; int main() { while(~scanf("%d%d",&n,&m)) { f[n][m]=0; ro(i,n,0) ro(j,m,0)if(i!=n||j!=m) f[i][j]= ( f[i+1][j]*(n-i)*j+ f[i][j+1]*i*(m-j)+ f[i+1][j+1]*(n-i)*(m-j)+n*m )/ ( n*m-i*j ); printf("%.4f\n",f[0][0]); } return 0; }