Loading

【笔记】概率与期望

P5104 红包发红包

\([0,w]\)中等概率发出一个红包,期望发出一个\(\dfrac{w}{2}\)的红包,即可以看成每次发出\(\dfrac{w}{2}\)的红包。答案就是 \(w\times 2^{-k}\)

Code


P7385 「EZEC-6」跳一跳

期望\(E=\sum p(x)v(x)\)

这题运用期望的线性性质,对于每个特殊格子,拆开算贡献。对于一般格子,推下式子然后矩阵乘法优化即可。

Code


P1850 [NOIP2016 提高组] 换教室

期望\(\rm DP\)入门题,\(f[i][j][0/1]\) 表示前 \(i\) 节颗,换了 \(j\) 次教室,上次有没有换教室的期望值,讨论一下转移即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 2005
#define M 305
using namespace std;
int d[N][N],n,m,t,e,u[N],v[N];double k[N];
double f[N][N][2];
int main(){
	memset(d,0x3f,sizeof(d));
	scanf("%d%d%d%d",&n,&m,&t,&e);
	rep(i,1,n)scanf("%d",&u[i]);
	rep(i,1,n)scanf("%d",&v[i]);
	rep(i,1,n)scanf("%lf",&k[i]);
	rep(i,1,e){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		d[x][y]=d[y][x]=min(d[x][y],z);
	}
	rep(r,1,t)d[r][r]=0;
	rep(r,1,t)rep(i,1,t)rep(j,1,t)d[i][j]=min(d[i][j],d[i][r]+d[r][j]);
	rep(i,1,n)rep(j,0,m)f[i][j][0]=f[i][j][1]=998244353.114;
	f[1][0][0]=f[1][1][1]=0;
	/*rep(i,1,t){
		rep(j,1,t)printf("%d ",d[i][j]);
		putchar('\n');
	}*/
	rep(i,2,n){
		rep(j,1,m){
			f[i][j][0]=min(
				f[i-1][j][1]+k[i-1]*d[u[i]][v[i-1]]+(1.00-k[i-1])*d[u[i]][u[i-1]],
				f[i-1][j][0]+d[u[i]][u[i-1]]);
			f[i][j][1]=min(
				f[i-1][j-1][0]+k[i]*d[v[i]][u[i-1]]+(1.00-k[i])*d[u[i]][u[i-1]],
				f[i-1][j-1][1]+k[i]*k[i-1]*d[v[i]][v[i-1]]+k[i]*(1.00-k[i-1])*d[v[i]][u[i-1]]
				+(1.00-k[i])*k[i-1]*d[u[i]][v[i-1]]+(1.00-k[i])*(1.00-k[i-1])*d[u[i]][u[i-1]]);
		}
		f[i][0][0]=f[i-1][0][0]+d[u[i-1]][u[i]];
	}
	double ans=998244353.00;
	rep(i,0,m)ans=min(ans,min(f[n][i][0],f[n][i][1]));
	printf("%.2lf\n",ans);
	return 0;
}

P3830 [SHOI2012]随机树

第一问,期望的现实意义。

当标本趋于无穷大时,或理想状态时,期望可以看作实际值。

这里定义 \(f[i]\) 表示 \(i\) 个叶子的平均深度,可以看作一颗所有叶子深度都为 \(f[i]\) 的树,所以有 \(f[i]=f[i-1]+\dfrac{2}{i}\)

第二问,期望的线性性质。

我们可以求出概率 \(f[i][j]\) ,表示 \(i\) 个节点 ,深度\(\ge j\)的概率,枚举一下左右子树的大小和深度转移即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 105
using namespace std;
namespace task1{
	double f[N];int n;
	void main(){
		scanf("%d",&n);
		f[1]=0;
		rep(i,2,n){
			f[i]=f[i-1]+2.00/(i);
		}
		printf("%.6lf\n",f[n]);
	}
}
namespace task2{
	double f[N][N];int n;
	void main(){
		scanf("%d",&n);
		rep(i,1,n)f[i][0]=1.00;
		rep(i,2,n)rep(j,1,i-1){
			rep(l,1,i-1)
				f[i][j]+=(f[l][j-1]+f[i-l][j-1]-f[l][j-1]*f[i-l][j-1]);
			f[i][j]/=i-1;
		}
		double ans=0;
		rep(i,1,n-1)ans+=f[n][i];
		printf("%.6lf\n",ans);
	}
}
int main(){
	int op;scanf("%d",&op);
	if(op==1)task1::main();
	else task2::main();
	return 0;
}

P4564 [CTSC2018]假面

期望的线性性质。

我们定义状态,求出 \(f[k][i][j]\) 表示 \(k\) 次操作后,第 \(i\) 个人剩余血量 \(j\) 的概率。

Code


P2473 [SCOI2008] 奖励关

期望\(\rm DP\)

期望\(\rm DP\)一般倒叙转移,概率\(\rm DP\)一般顺序转移。

因为对于当前状态\(f[i]\),能转移到\(f[a_1],f[a_2],\cdots\)的概率是相等的或易于计算的,顺序递推概率即可。

而计算期望的时候,我们不仅要考虑转移的概率,还需要考虑当前状态出现的概率。而我们倒叙转移可以忽略当前状态出现的概率。

这里定义\(f[i][S]\)表示前\(i-1\)个人选过的关卡集合为\(S\)的最大价值。\(f[i+1][S|2^k]\to f[i][S]\)

Code


P2221 [HAOI2012]高速公路

很明显的序列\(\rm DS\)题,根据期望的定义,我们求出所有可能的权值之和,除以方案数,就是期望。

Code


P3239 [HNOI2015]亚瑟王

这道题的关键在于攻击一次后一轮结束并将牌弃掉。

首先,\(P_{\texttt{打出}}=1-P_{\texttt{没打出}}\)

打出的概率不好计算,但是没有打出的概率很好计算,就是一直将牌捏在手里。

第一张牌,需要选择\(r\)次,没有打出的概率为\((1-P_i)^r\)

\(i\) 张牌,如果前面一直没有打出牌,则也需要选择 \(r\) 次,否则前面打出多少牌,就要少选多少次。

那么我们可以定义 \(f[i][j]\) 表示前 \(i\) 张牌打出 \(j\) 张的概率,分打出第 \(i\) 张牌和不打出第 \(i\) 张牌讨论。有转移方程

\[f[i][j]=f[i-1][j-1]\times(1-(1-P)^{r-j-1})+f[i-1][j]\times (1-P)^{r-j} \]

最后对每张牌统计贡献即可。

Code


P3750 [六省联考2017]分手是祝愿

思维题。

首先考虑最小化操作数,一个想法是从高位向低位依次熄灭即可。

事实上这就是最小代价,并且是唯一的。因为任何一个按键都是唯一的,不能通过其他组合键替换,所以我们要关掉最高位的灯,就必须按它。

既然是唯一的,那么我们就得到了必须按的按钮。那么题目也就很清晰了,每次操作,可能按到必须键,也可能按了非必须键。

定义状态 \(f[i]\) 表示还有 \(i\) 个必须键的期望次数,不难得到等式

\[f[i]=1+\dfrac{i\times f[i-1]}{n}+\dfrac{(n-i)\times (f[i+1]+f[i])}{n} \]

看上去可以高斯消元,但是时间过不去。

一个巧妙的转换是利用差分,定义 \(g[i]\)\(f[i]-f[i-1]\),则有方程

\[g[i]=\dfrac{i}{n}+\dfrac{(n-i)\times (g[i+1]+g[i]+1)}{n} \]

这就可以线性转移了。

Code


P4284 [SHOI2014]概率充电器

我们考虑当前点 \(i\) ,如果它被点亮,可能是被树上任意一个点亮,不好计算。

考虑容斥,我们求出它不被点亮的概率,就是所有点都无法点亮它。

我们以当前点为根,定义 \(f[i]\) 表示以节点 \(i\) 为根的子树中,没有点亮 \(i\) 的概率。

很显然点亮不能跨越子树,因为跨越子树后,子树的根一定会被点亮,所以这样做是对的,\(\rm DP\)下去即可。

要求出以任意点为根的情况,直接二次扫描换根即可。

Code


P5249 [LnOI2019]加特林轮盘赌

定义 \(f[i]\) 表示 \(i\) 个人中剩下第一个人的概率。

首先这一轮中第一个人不能死,然后枚举后面死了哪些人,可以得到等式

\[f[i]=\sum\limits_{j=0}^{i-1}\binom{i-1}{j}P^j(1-P)^{i-j-1}f[i-j] \]

直接做是\(\rm O(N^2)\),还可以二项式反演的样子。

但是这道题不取模,直接组合数会爆精度,所以我们考虑换一种方法。

定义 \(f[i][j]\) 表示 \(i\) 个人中剩第 \(j\) 个人的概率。

对于\(j\ge 2\),有 \(f[i][j]=P\times f[i-1][j-1]+(1-P)\times f[i][j-1]\)

对于\(j=1\),有\(f[i][j]=(1-P)\times f[i][i]\)

直接消元是 \(\rm O(N^4)\) 的,但是发现每个式子只有两个未知数,直接迭代即可做到\(\rm O(N^2)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 10005
using namespace std;
int n,k;
double p,f[2][N];
int main(){
	//freopen("INPUT","r",stdin);
	scanf("%lf%d%d",&p,&n,&k);
	if(p==0){if(n==1)puts("1");else puts("0");return 0;}
	f[1][1]=1;
	rep(i,2,n){
		int cur=i&1,pre=cur^1;
		memset(f[cur],0,sizeof(f[cur]));
		double u=0,v=0;
		pre(j,i,2)v+=(u+1)*p*f[pre][j-1],u=(u+1)*(1-p);
		f[cur][1]=(1-v)/(u+1);
		rep(j,2,i)f[cur][j]=(1-p)*f[cur][j-1]+p*f[pre][j-1];
	}
	printf("%.10lf\n",f[n&1][k]);
	return 0;
}
posted @ 2021-12-16 17:24  7KByte  阅读(192)  评论(0编辑  收藏  举报