[水题日常]UVA11181 条件概率(Probability|Given)
- 话说好久没写blog了
- 好好学概率论的第一天,这题一开始完全不会写,列出个条件概率的公式就傻了,后来看着lrj老师的书附带的代码学着写的…
因为我比较弱智一些比较简单的东西也顺便写具体点或者是按照书上的说法写了(所以这一篇可能还会更偏向于笔记的样子…)…如果觉得没必要看的话可以直接跳下去
题意:\(n\)个人准备去买东西,第\(i\)个人买东西的概率是\(P_i\),买完东西之后你得知一共有\(r\)个人买了东西,求每个人实际买了东西的概率。(\(1<=n<=20\))
- 记\(r\)个人买了东西的事件为\(E\),第\(i\)个买东西的事件为\(E_i\),则我们要求的是\(P(E_i|E)\)
- 条件概率公式告诉我们\(P(E_i|E)=\frac {P(E_i E)}{P(E)}\) (\(P(E_i E)\)表示事件\(E_i\)和\(E\)同时发生的概率)
- 先来看看\(P(E)\)怎么求?首先注意到这题的\(n\)只有20我们直接枚举所有购买的情况,用一个数组\(buy[1..n]\)记录每个人是否买了东西(比如1表示买0表示没买)
- 直接\(dfs\)所有情况,枚举完一种有\(r\)个人购买的情况之后给\(P(E)\)加上这一种情况的概率:\(\prod_{i\in X} P_i\cdot\prod_{j\in Y} (1-P_j)\)(这里用\(X\)来表示所有买东西的人的集合,\(Y\)表示没有买东西的人的集合)(不要被这个东西吓到!,想想看假如3个人里有两个人买了东西,分别是第一个人和第三个人,那么这一种情况的概率是不是就是\(P_1\cdot(1-P_2) \cdot P_3\)?!)
- (上面那一串式子直接在搜的时候就顺便求出来)
- 啃完了硬骨头
(对我来说是这样的)我们再来看看\(P(E_i E)\),跟上面的方法一样,枚举的时候顺便记录数组\(buy[]\),枚举完一种买\(r\)个东西的情况之后扫一遍\(buy[]\),如果记\(sum[i]\)表示最后的\(P(E_i E)\)的话,那样如果第\(i\)个人买了就给\(sum[i]\)加上上面那一串式子 - 最后对于每个询问输出\(sum[i]/P(E)\)就好啦~
最后还是贴一下代码吧(然而这个只是学着书上附带的代码自己打了一遍(写的应该还比较挫
要往下拉的请慎重!~
#include<cstdio>
#include<cstring>
int kase,n,r;
bool buy[25];double p[25],sum[25];
inline void dfs(int d,int c,double prob)
{
if(c>r||d-c>n-r+1)return;
if(d==n+1)
{
sum[n+1]+=prob;
for(int i=1;i<=n;i++)if(buy[i])
sum[i]+=prob;
return;
}
buy[d]=0;
dfs(d+1,c,prob*(1-p[d]));
buy[d]=1;
dfs(d+1,c+1,prob*p[d]);
}
int main()
{
while(scanf("%d%d",&n,&r)==2&&(n||r))
{
memset(sum,0,sizeof(sum));
memset(buy,0,sizeof(buy));
for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
dfs(1,0,1.0);
printf("Case %d:\n",++kase);
for(int i=1;i<=n;i++)printf("%.6lf\n",sum[i]/sum[n+1]);
}return 0;
}
这里直接顺便用\(sum[n+1]\)表示了\(P(E)\)
顺便,可以注意一下这里\(dfs\)的
if(c>r||d-c>n-r+1)return;
第一个就是判断买的人有没有超过\(r\),超过的话直接剪枝,第二个判断这条路径上没有买的人是否超过应该没有买的人,超过的话也剪掉。(这样其实也保证了\(d=n+1\)时\(c=r+1\))
如果有问题欢迎在评论区指出~