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