poj 2151 Check the difficulty of problems
阿新 • • 發佈:2018-12-16
蒟蒻難得做一道概率dp,然後除了容斥出至少對一道的情況然後啥都不會了。。
題目大意:
有T個隊,m道題,問每個隊至少對一道題且冠軍隊至少對n道題的概率
dp[i][j][k]表示第i個隊做前j道題對k道的概率,由上一個轉移過來
做對這個dp[i][j][k]+=dp[i][j-1][k-1]*p[i][j],沒做對dp[i][j][k]+=dp[i][j-1][k]*(1-p[i][j])
現在我們可以得到每個隊做對幾道的概率了,然後考慮這個問題
每個隊做對至少一題,可以看作每個隊都做不了一道題的情況的補集,用1-所有的s[i][0]即可
現在考慮冠軍隊要至少做對n道題的概率,即存在至少一個隊,做對了n道以上的題
還是用補集轉化的思想,可以看作是所有隊都只做對了1~n-1道題的補集,用1-這個值,就可以得到至少一個隊做對了n道題,滿足冠軍隊做了至少n道題的要求
用s[i][j]表示i隊做對j題的概率,那麼s[i][j]=sigma(dp[i][k][j]) j<=k<=m
用ans1表示至少做對一道,則ans1*=(1-s[i][0])
用ans2表示所有隊都在1~n-1題中,則ans2*=(s[i][n]-s[i][0])
用ans1-ans2就可得到答案
程式碼如下
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #define db double using namespace std; db dp[1005][55][55]; db s[1005][55]; db p[1005][55]; int n,m,T; void init() { memset(dp,0,sizeof(dp)); memset(s,0,sizeof(s)); memset(p,0,sizeof(p)); } int main() { while (scanf("%d%d%d",&m,&T,&n)!=EOF) { init(); if (!n && !m && !T) break; 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++) { dp[i][0][0]=1.0; for (int j=1;j<=m;j++) { dp[i][j][0]=dp[i][j-1][0]*(1.0-p[i][j]); } } for (int i=1;i<=T;i++) { for (int j=1;j<=m;j++) { for (int k=1;k<=j;k++) { dp[i][j][k]=dp[i][j-1][k-1]*p[i][j]+dp[i][j-1][k]*(1.0-p[i][j]); } } } for (int i=1;i<=T;i++) { for (int j=0;j<=n;j++) { for (int k=0;k<=j;k++) { s[i][j]+=dp[i][m][k]; } } } /* for (int i=1;i<=T;i++) { s[i][0]=dp[i][m][0]; for (int j=1;j<=m;j++) { for (int k=1;k<=m;k++) { s[i][k]=s[i][k-1]+dp[i][m][k]; } } }*/ db ans1=1.0; for (int i=1;i<=T;i++) { ans1*=(1.0-s[i][0]); } db ans2=1.0; for (int i=1;i<=T;i++) { ans2*=(s[i][n-1]-s[i][0]); } printf("%0.3lf\n",ans1-ans2); } return 0; }