题目链接:点击打开链接
题意: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); } }