UVa 11181 Probability|Given (概率&枚举组合)

题意

n n n个人准备去超市逛,其中第 i i i个人买东西的概率是 P i P_i Pi。 逛完以后你得知有 r r r个人买了东西。 根据这一信息,请计算每个人实际买了东西的概率。 输入 n ( 1 ≤ n ≤ 20 ) n(1\leq n \leq 20) n(1n20) r ( 0 ≤ r ≤ n ) r(0\leq r\leq n) r(0rn),输出每个人实际买了东西的概率。

解题思路

这道题的思路紫书上讲的很清楚了,这里我用自己的语言再转述一遍。设 P ( E ) P(E) P(E) r r r个人买了东西的概率, P ( E i ) P(E_i) P(Ei)为第 i i i个人买了东西的概率。那么根据题意,所求的概率为 P ( E i ∣ E ) P(E_i|E) P(EiE),也就是在 E E E的情况下 E i E_i Ei出现的概率。那么设 S S S为所有 n n n r r r的组合,那么 P ( E ) = ∑ s ∈ S ∏ e i ∈ s p ( e i ) P(E) = \sum\limits_{s \in S} \prod\limits_{e_i \in s}p(e_i) P(E)=sSeisp(ei) P ( E i ) = P(E_i)= P(Ei)= 所有包含第 i i i个人的组合的概率。

技巧

对于枚举组合,我看很多题解都是用的dfs,这里我推荐一个从《挑战程序设计竞赛》里看到的枚举组合的方法,设要取的组合为 ( n r ) {n\choose r} (rn),那么可以通过以下的代码得到这个状态的二进制形式:

void combination(int n, int r) {
    int comb = (1 << r) - 1;
    while (comb < 1 << n) {
        // 处理组合状态
        int x = comb & -comb, y = comb + x;
        comb = ((comb & ~y) / x >> 1) | y;
    }
}

这样就可以不用写dfs了ヾ(@@)ノ

时间复杂度

O ( ( n r ) ∗ n ) O({n\choose r}*n) O((rn)n)

代码

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

typedef long long ll;

const int INF = 2147483647;
const int INF2 = 0x3f3f3f3f;
const ll INF64 = 1e18;
const double INFD = 1e30;
const double EPS = 1e-6;
const double PI = 3.1415926;
const ll MOD = 1e9 + 7;

int n, m, k;
int CASE;
const int MAXN = 22;
double arr[MAXN];
double ans[MAXN];
int main() {
#ifdef LOCALLL
    freopen("in", "r", stdin);
    freopen("out", "w", stdout);
#endif
    int r;
    while (~scanf("%d %d", &n, &r) && n) {
        for (int i = 0; i < n; i++) {
            scanf("%lf", &arr[i]);
        }
        memset(ans, 0, sizeof(ans));
        double tot = 0.0;
        if (r > 0) {
            int comb = (1 << r) - 1;
            while (comb < 1 << n) {
                double tmp = 1.0;
                for (int i = 0; i < n; i++) {
                    if ((comb >> i) & 1) {
                        tmp *= arr[i];
                    } else {
                        tmp *= 1.0 - arr[i];
                    }
                }
                tot += tmp;
                for (int i = 0; i < n; i++) {
                    if ((comb >> i) & 1) {
                        ans[i] += tmp;
                    }
                }
                int x = comb & -comb, y = comb + x;
                comb = ((comb & ~y) / x >> 1) | y;
            }
        } else {
            tot = 1;
        }
        printf("Case %d:\n", ++CASE);
        for (int i = 0; i < n; i++) {
            printf("%.6lf\n", ans[i] / tot);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值