题意:

给定每个题可以解出(独立和偷听)的概率,求解出0~12题的概率。

思路:

首先可以算出每个题解出的概率,即1-解不出该题的概率,即

///计算独立的每道题解出来的概率
    for(int i=1;i<=12;i++){
        double tmp=(1-a[i])*(1-b[i])*(1-c[i]);
        ac[i]=1-tmp;
    }

再考虑如何计算解出0~12题的概率。因为数据范围很友好,可以用dfs或状压枚举或dp。

思路一:DP

考虑用dp[i] [j]表示只做前i个题做对了j个题的概率。那么很容易可以推出分界点就是第i个题是否做对。

如果第i个题做不对,那么dp[i] [j]就可以由dp[i-1] [j]转移而来。

如果第i个题做的对,那么dp[i] [j]就可以由dp[i-1] [j-1]转移而来。

 dp[i][j]=dp[i-1][j]*(1-ac[i])+dp[i-1][j-1]*ac[i];

要注意初始化dp[0] [0] =1,表示前0个题做对0个题的概率是1

以及转移过程中对j==0的特殊处理。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=15;
double a[maxn],b[maxn],c[maxn];
double dp[maxn][maxn];
double ac[maxn];///一道题解出来的概率

int main(){
    for(int i=1;i<=12;i++) cin>>a[i];
    for(int i=1;i<=12;i++) cin>>b[i];
    for(int i=1;i<=12;i++) cin>>c[i];

    ///dp[i][j]表示只做前i个题做对了j个题的概率

    ///计算独立的每道题解出来的概率
    for(int i=1;i<=12;i++){
        double tmp=(1-a[i])*(1-b[i])*(1-c[i]);
        ac[i]=1-tmp;
    }

    dp[0][0]=1;
    for(int i=1;i<=12;i++){
        dp[i][0]=dp[i-1][0]*(1-ac[i]);
        for(int j=1;j<=i;j++){
            dp[i][j]=dp[i-1][j]*(1-ac[i])+dp[i-1][j-1]*ac[i];
        }
    }
    for(int i=0;i<=12;i++){
        printf("%.6f\n",dp[12][i]);
    }

    return 0;
}

思路二:状压枚举

用二进制数表示每道题是否解出的状态,在每一种状态下枚举每一道题是否被解出,计算相应的概率即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=15;
double a[maxn],b[maxn],c[maxn];
double dp[maxn];
double ac[maxn];///一道题解出来的概率

int main(){
    for(int i=0;i<12;i++) cin>>a[i];
    for(int i=0;i<12;i++) cin>>b[i];
    for(int i=0;i<12;i++) cin>>c[i];

    ///计算独立的每道题解出来的概率
    for(int i=0;i<12;i++){
        double tmp=(1-a[i])*(1-b[i])*(1-c[i]);
        ac[i]=1-tmp;
    }

   for(int i=0;i<(1<<12);i++){
       int tmp=0;
       double tt=1;
       for(int j=0;j<12;j++)
           if((1<<j)&i){
               tmp++;
               tt*=ac[j];
           }
           else tt*=(1-ac[j]);
       dp[tmp]+=tt;
   }
    for(int i=0;i<=12;i++)
        printf("%.6f\n",dp[i]);

    return 0;
}

思路三:DFS

跟状压枚举的思路基本一致,只是换了种实现形式。具体可参考:传送门