POJ - 2151 Check the difficulty of problems (概率DP)

题目链接:点击打开链接

题意:M支队伍,T个队伍。给出每个队伍写出某道题的概率。求所有队伍都写出题的同时冠军队伍写题不少于N道的概率。

题解:这道题虽然是概率DP但是,难点不在于DP方程的,而在于如何求这个题意中的概率。

所有队伍都写出题,表示每支队友都至少写出1题。

所以我们只需要求出每支队伍写不出题的概率。1-这个概率,就是这支队伍写出题的概率,把所有这个概率相乘,就是所有队伍写出题的概率。

当时陷入了一个误区:1-所有队伍都写不出来题的概率 = 所有队伍写出来题的概率。

为什么是错的呢? 举个例子:

1道题,两支队伍。

那么会有以下几种概率:

0 0 两支队伍都没写出来题

0 1 或 1 0 至少一支队伍写出来题

1 1 每支队伍都写出来题

我们就能发现为什么 这个是不对了。


我们会发现之前已经求了每支队伍至少写1道题的概率。

这个概率事件包括:A队写出 1, 2, 3, 。。。M道。

                                 B队写出 1,2, 3,。。。M道。

                                 。。。

在同时又要满足冠军至少写 N 到题。换角度思考一下,冠军至少写N题的对立事件 就是 所有人同时都写不出来N的概率。

就等价于所有人同时写出来1 -- (n-1)题的概率。

问题思考到这里就转化成:每队均至少做一题的概率P1 减去 每队做题数均在1到N-1之间的概率P2

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】);

看代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

double f[1005][35],dp[1005][35][35];
double ans,tmp,sum;
int N,M,T;
int main(){
	while(~scanf("%d%d%d",&M,&T,&N)){
		if(M == 0 && N == 0 && T == 0) break;
		for(int i = 1 ; i <= T ; i ++)
			for(int j = 1; j <= M ; j ++)
				scanf("%lf",&f[i][j]);
		memset(dp,0,sizeof(dp));
		for(int i = 1; i <= T ; i ++)
			dp[i][0][0] = 1;
		for(int i = 1; i <= T ; i ++){
			for(int j = 1; j <= M ; j ++){
				for(int k = 0 ; k <= j ; k ++){
					dp[i][j][k] += dp[i][j-1][k-1] * f[i][j];
					dp[i][j][k] += dp[i][j-1][k] *(1-f[i][j]);
				}
			}
		}
		ans = tmp = 1;
		for(int i = 1; i <= T ; i ++)
			ans *= (1 - dp[i][M][0]);
		for(int i = 1; i <= T ; i ++){
			sum = 0;
			for(int j = 1; j < N ; j ++)
				sum += dp[i][M][j];
			tmp *= sum;
		}
		ans -= tmp;
		printf("%.3lf\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/PK__PK/article/details/79992216