http://poj.org/problem?id=2151 (题目链接)
题意
T支队伍,一共M道题,第i支队伍解出第j道题的概率为p[i][j]。问每支队伍至少解出1道题并且解题最多的的队伍至少解出N道题的概率。
Solution
我终于明白了,这种题目,永远也不要想着去搞清楚样例是怎么算出来的,只要列出你认为正确的dp方程就行了。。
于是这道题,令${f_{i,j,k}}$表示第i个队伍,前j道题,解出k道的概率。转移很显然:
$${f_{i,j,k}=f_{i,j-1,k-1}*p_{i,j}+f_{i,j-1,k}*(1-p_{i,j})}$$
再令${s_{i,j}}$表示第i个队伍解出的题目数小于等于j道的概率,那么很显然:
$${s_{i,j}=s_{i,j-1}+f_{i,m,j}}$$
于是每个队伍解出的题目数再范围${1,M}$的概率p1(也就是至少解出一道题):
$${p1=prod_{i=1}^{t}(1-s_{i,0})}$$
每个队伍解出的题目数在范围${1,N-1}$的概率p2(每个队伍都至少解出一道题,但没有一支队伍解题数大于等于N):
$${p2=prod_{i=1}^{t}(s_{i,N-1}-s_{i,0})}$$
那么最后的答案:p1-p2
代码
// poj2151 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define inf 1<<30 #define eps 1e-8 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxt=1010,maxm=40; double f[maxt][maxm][maxm],s[maxt][maxm],p[maxt][maxm]; int n,m,t; int main() { while (scanf("%d%d%d",&m,&t,&n)!=EOF && m && t && n) { memset(f,0,sizeof(f)); for (int i=1;i<=t;i++) for (int j=1;j<=m;j++) scanf("%lf",&p[i][j]); for (int i=1;i<=t;i++) { f[i][0][0]=1; for (int j=1;j<=m;j++) for (int k=0;k<=j;k++) { f[i][j][k]=f[i][j-1][k]*(1-p[i][j]); if (k) f[i][j][k]+=f[i][j-1][k-1]*p[i][j]; } s[i][0]=f[i][m][0]; for (int j=1;j<=m;j++) s[i][j]=s[i][j-1]+f[i][m][j]; } double p1=1,p2=1; for (int i=1;i<=t;i++) p1*=1-s[i][0],p2*=(s[i][n-1]-s[i][0]); printf("%.3lf ",p1-p2); } return 0; }