1. 程式人生 > >poj 2151 Check the difficulty of problems

poj 2151 Check the difficulty of problems

蒟蒻難得做一道概率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;
}