题意
有 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(1≤n≤20)和 r ( 0 ≤ r ≤ n ) r(0\leq r\leq n) r(0≤r≤n),输出每个人实际买了东西的概率。
解题思路
这道题的思路紫书上讲的很清楚了,这里我用自己的语言再转述一遍。设 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(Ei∣E),也就是在 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)=s∈S∑ei∈s∏p(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;
}