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;
}

猜你喜欢

转载自blog.csdn.net/beloved_rancy/article/details/83240310