概率期望基础

UVA1636 决斗 Headshot

description

给出一个大小为(n)(01) 环,初始时你在环上权值为(0) 的某个位置,询问下一个位置还是(0) 的概率大还是随机在环上选择一个位置的概率大。

solution

对于第一问,其实是一个条件概率,由公式可以直接得到答案等于(00) 的总数除以(0) 的总数。

而第二问直接算就可以了。

code

#include<bits/stdc++.h>
using namespace std;
const int N=105;
char ch[N];
int main()
{
	while(scanf("%s",ch+1)!=EOF)
	{
		int n=strlen(ch+1);
		ch[n+1]=ch[1];
		int c0=0,c00=0;
		for(int i=1;i<=n;++i)
		{
			if(ch[i]=='0')++c0;
			if(ch[i]=='0'&&ch[i+1]=='0')++c00;
		}
		if(c0*c0>n*c00)puts("ROTATE");
		else if(c0*c0==n*c00)puts("EQUAL");
		else puts("SHOOT");
	}
	return 0;
}

UVA10491 奶牛和轿车 Cows and Cars

description

(a+b) 个门,其中(a) 个门后面是奶牛,(b) 个门后面是汽车。现在你先随机选择一个门,然后主持人会帮你打开你没有选择的门中(k) 个门后是奶牛的门。这时,排除方才你选的门和已经打开的门,你再从剩下的门中随机选择一个,问这个门背后是汽车的概率。

solution

考虑全概率公式。假设最开始选择的门后面是奶牛,则概率为(frac{a}{a+b}cdotfrac{b}{a+b-k-1}) 。否则,概率为(frac b{a+b}cdotfrac{b-1}{a+b-k-1}) 。二者相加就是最终的答案。

code

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int main()
{
	while(scanf("%d%d%d",&n,&m,&k)==3)
	{
		int x=m*(m-1)+n*m;
		int y=(n+m)*(n+m-k-1);
		printf("%.5lf
",1.0*x/y);
	}
	return 0;
}

UVA11181 条件概率 Probability|Given

description

(n) 个人要去买东西,他们去买东西的概率为(p_i)

现在得知有(r) 个人买了东西,在这种条件下,求每个人买东西的概率。

solution

根据公式

[P(A|B)=frac{P(AB)}{P(B)} ]

我们需要算两个东西:有(r) 个人去买东西的概率以及第(i) 个人去买了东西同时有(r) 个人去买东西的概率。注意到(n) 很小,于是我们可以枚举每个人买还是不买,然后计算相应概率,累加答案即可。

code

#include<bits/stdc++.h>
using namespace std;
const int N=25;
int n,m,kase;double p[N],ans[N];
int main()
{
	while(scanf("%d%d",&n,&m)==2&&n)
	{
		for(int i=0;i<n;++i)scanf("%lf",p+i),ans[i]=0;
		int st=1<<n;double tot=0;
		for(int i=0;i<st;++i)
		{
			int cnt=0;double pob=1.0;
			for(int j=0;j<n;++j)
				if(i&(1<<j))++cnt,pob*=p[j];
				else pob*=1-p[j];
			if(cnt!=m)continue;
			tot+=pob;
			for(int j=0;(1<<j)<=i;++j)
				if(i&(1<<j))ans[j]+=pob;
		}
		printf("Case %d:
",++kase);
		for(int i=0;i<n;++i)printf("%.6lf
",ans[i]/tot);
	}
	return 0;
}

UVA1637 纸牌游戏 Double Patience

solution

容易想到的做法是直接爆搜,带(9) 个参数分别表示每堆牌还剩下多少,每次就两两枚举考虑能否操作同时计算概率即可。然后正解做法就是带上记忆化。

具体实现的话可以直接开九维数组,也可以状压。直接状压似乎也不太好写,但是如果改为(map) ,每个节点存(9) 个值,代码就会简洁不少。不过我似乎没有这么写

code

#include<bits/stdc++.h>
using namespace std;
const int N=9,M=5;
char s[N][M],ch[5];
typedef vector<int> vec;
double f[M][M][M][M][M][M][M][M][M];
bool vis[M][M][M][M][M][M][M][M][M];
inline bool read()
{
	for(int i=0;i<N;++i)
		for(int j=1;j<M;++j)
			if(scanf("%s",ch)==EOF)return false;
			else s[i][j]=ch[0];
	return true;
}
double dfs(const vec&a)
{
	bool flag=1;
	for(int i=0;i<N;++i)if(a[i]>0){flag=0;break;}
	if(flag)return 1.0;
	double&d=f[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]];
	if(vis[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]])return d;
	vis[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]]=1;
	vec b=a;d=0;int cnt=0;
	for(int i=0;i<N;++i)
		for(int j=i+1;j<N;++j)
			if(b[i]>0&&b[j]>0&&s[i][a[i]]==s[j][a[j]])
			{
				++cnt;--b[i],--b[j];
				d+=dfs(b);
				++b[i],++b[j];
			}
	if(!cnt)return d=0;
	d/=1.0*cnt;
	return d;
}
int main()
{
	while(read())
	{
		vec v;v.clear();
		for(int i=0;i<N;++i)v.push_back(4);
		memset(vis,0,sizeof vis);
		printf("%.6lf
",dfs(v));
	}
	return 0;
}

UVA11021 Tribles

description

一开始有(k) 个生物,每个生物只能活(1) 天,死的时候有(p_i) 的概率产生(i) 只这种生物(也只能活一天),询问(m) 天内所有生物都死的概率(包括(m) 天前死亡的情况)

solution

不妨设(f_i) 表示初始时只有一个生物,在(i) 天以内所有生物都死亡的概率。最终答案就是(f_m^k)

考虑转移。容易发现考虑第(i-1) 天的情况过于复杂,于是可以考虑第一天的情况,根据全概率公式不难发现:

[f_i=sum_{j=0}^{n-1}p_jf_{i-1}^j ]

直接递推即可。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,k,m,kase;double p[N],f[N];
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&n,&k,&m);
		for(int i=0;i<n;++i)scanf("%lf",p+i);
		f[1]=p[0];
		for(int i=2;i<=m;++i)
		{
			f[i]=0;double tmp=1.0;
			for(int j=0;j<n;++j,tmp*=f[i-1])f[i]+=tmp*p[j];
		}
		printf("Case #%d: %.7lf
",++kase,pow(f[m],k));
	}
	return 0;
}

UVA11427 Expect the Expected

description

(n) 个晚上能玩游戏,每个晚上最多可以玩 (1) 局游戏,获胜概率为 (p)
当一个晚上获胜概率严格大于 (p) 时, 就会高高兴兴结束, 否则垂头丧气结束。
如果某个晚上垂头丧气结束, 那么以后的晚上就不会玩游戏了, 否则第二天晚上继续玩游戏。
求期望会玩多少天的游戏。

solution

首先我们有经典结论:

如果事件(A) 发生的概率为(frac 1Q) ,则期望下重复(Q) 次事件(A) 就会发生一次。

有了这个结论后我们只需要考虑一个晚上垂头丧气的概率。

考虑(dp) 。记(d_{i,j}) 代表考虑玩了(i) 局,胜利了(j) 局且始终没有高高兴兴结束的概率,那么有

[d_{i,j}=0(frac ji>p)\ d_{i,j}=pcdot d_{i-1,j-1}+(1-p)d_{i-1,j}(frac jile p) ]

最终的概率就是(ans=sum_{i=0}^nd_{n,i})

code

#include<bits/stdc++.h>
using namespace std;
const int N=105;
typedef double db;
int a,b,n,kase;
db d[N][N];
inline int read()
{
	int s=0;char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s;
}
int main()
{
	int T=read();
	while(T--)
	{
		a=read(),b=read(),n=read();
		memset(d,0,sizeof d);
		db p=1.0*a/b;d[0][0]=1;
		for(int i=1;i<=n;++i)
			for(int j=0;j<=i;++j)
			{
				if(j*b>i*a)break;
				d[i][j]=(1.0-p)*d[i-1][j];
				if(j)d[i][j]+=p*d[i-1][j-1];
			}
		db ans=0;
		for(int i=0;i<=n;++i)ans+=d[n][i];
		printf("Case #%d: %d
",++kase,(int)(1/ans));
	}
	return 0;
}

UVA11762 Race to 1

description

给定一个整数(N) ,每次从不超过(N) 的素数中等概率随机选一个(p)(pmid N) ,则令(N = frac Np) , 否则不变,问期望几次能够把 (N) 变成(1)

solution

(f_i) 表示将(i) 变成(1) 的期望次数,不妨设(s) 表示不大于(i) 的素数个数,(c) 表示不大于(i) 且能整除(i) 的素数个数,那么

[f_i=1+frac{s-c}scdot f_i+sum_{jmid i,jin Prime}frac{f_{frac ij}}{s}\ f_i=frac{sum_{jmid i,jin Prime}{f_{frac ij}}+s}c ]

直接记忆化后递推即可。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
bool vis[N];int n;double f[N];
bool flag[N];int pr[N],pcnt,num[N];
inline void pre(int mx)
{
	flag[1]=1;
	for(int i=2;i<=mx;++i)
	{
		if(!flag[i])pr[++pcnt]=i;
		for(int j=1;j<=pcnt;++j)
		{
			int now=pr[j]*i;if(now>mx)break;
			flag[now]=1;
			if(i%pr[j]==0)break;
		}
	}
	for(int i=1;i<=mx;++i)num[i]=num[i-1]+(!flag[i]);
}
double dfs(int now)
{
	if(now==1)return 0;
	if(vis[now])return f[now];
	vis[now]=1;
	if(!flag[now])return f[now]=num[now];
	int cnt=0;
	for(int i=2;i*i<=now;++i)
	{
		if(now%i)continue;
		if(!flag[i])++cnt,f[now]+=dfs(now/i);
		if(i*i!=now&&!flag[now/i])++cnt,f[now]+=dfs(i);
	}
	f[now]+=num[now];f[now]/=cnt;
	return f[now];
}
int main()
{
	pre(1e6);
	int T,kase=0;scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		printf("Case %d: %.10lf
",++kase,dfs(n));
	}
	return 0;
}

UVA1390 互联 Interconnect

description

输入(n) 个点(m) 条边的无向图(G) 。每次随机加一条非自环的边((u,v)) (加完后可以出现重边)。添加每条边的概率是相等的,求使(G) 联通的期望操作次数

data range

(nle 30,mle 1000)

solution

对于一个图,我们真正关心的是有多少联通块以及每个联通块的大小,并不关心每个点具体属于哪个联通块。

于是状态数等于(30) 的划分数。这个数在(5000) 左右,可以接受。

考虑如何转移。首先在联通块内部连是没有用处的。其次,注意到对于两个联通块(a,b) ,只要将(a,b) 联通的边都是等价的,都可以转移到新的相同状态。于是每个状态的转移也被限制在不大的范围内,可以接受。

初始状态就直接(dfs) 或者并查集乱搞即可。

总结

首先期望线性性,条件概率公式,全概率公式和全期望公式是需要掌握的,作为理论基础。

另外,用(dp) 的方式计算概率期望也是十分常见的。(概率(dp) 通常正推,期望(dp) 通常反推)

最后注意有时算期望要回归期望的定义式。

还有一个小技巧,即如果总情况数有限,则可以将一个概率问题转化为计数问题,使得抽象问题变得具体。

NO PAIN NO GAIN
原文地址:https://www.cnblogs.com/zmyzmy/p/14617557.html