zoukankan      html  css  js  c++  java
  • 概率期望

    概率与期望dp

    摘自gzz学长的讲义。

    线性性

    X, Y为两随机变量

    E(X + Y) = E(X) + E(Y)

    做一些期望题时,我们通常把题目所求的分开算期望,最后加一块,这样能大大降低难度。

    例一:TC SRM 595 Div1 Problem 900: Constellation

    • 题意

    n个点,每个点有⼀定概率出现,问凸包的期望⾯积。⽆重复点和三点共线。

    n<=50。

    • 解析

    前置技能:(计算几何)三角剖分求面积

    枚举每一条边所代表的面积对答案的贡献(其右侧点不能背选),然后加一块。

    例二:P1850 换教室

    • 题意

    有n堂课需要按顺序听,每堂课有两个教室可以选。

    所有的教室都在⼀个连通带权⽆向图上。

    下了⼀节课,必须要从这节课所在的教室赶往下节课所在的教室。

    ⼀开始这n堂课已经选好了默认的教室。

    你可以提出最多m个申请,申请把某堂课的教室换到另⼀个。

    申请有成功和失败的可能,会给出成功率。

    必须⼀次性提出所有申请,不能⽤之前的申请结果来决定之后的申请策略。

    最⼩化需要跑的路程的期望。

    n ≤ 2000,m ≤ 2000,教室数⽬≤ 300

    • 解析

    比较不错的期望dp练手题,可以加深对期望的理解。分九种情况大力讨论即可。

    值得注意的是,如果当前申请,那么不能是

    当前申请成功率 * min(上回申请, 上回不申请) + 当前申请失败率 * min(上回申请, 上回不申请)

    因为当前申请时不知道能不能成功,不能探知上回申请和不申请哪一个更优,因此不能同时乘上上回的最优答案,应该拆成

    min(当前申请且上回申请, 当前申请且上回不申请)

    即比较暴力的分类讨论。

    Code

    例三:TC SRM 561 Div1 Problem 1000: Orienteering

    • 题意

    给⼀棵不超过2500个点的树,树上有不超过300个关键点,

    从这些关键点中随机选择k个,

    然后你任意从⼀个点出发,⽤最短的路程遍历所有的这k个点,

    问你的路程的期望⻓度。

    • 解析

    需要一些小技巧。

    容易看出要建虚树。

    固定选点,答案为 边长和的二倍 - 直径长。

    我们分开来考虑。

    边长和二倍比较简单,树形dp可做。

    直径长也可以枚举直径端点,算概率,进而算期望。

    直径万一有好几条怎么办?我们可以给边权加第二维“扰动”,把每条边加上个0.0000000001 * rand(),这样就很难有多条直径了。对于这道题,用浮点数会稍微麻烦些,可以用pair代替。

    这种方法同样适用于其他题中,如计算几何中加个 eps 防止有三点共线 or 两点横/纵坐标相同。但要把握好度,别影响正确性。


    随机游走

    求从开始到结束的期望步数

    • 做法1:设f[a]为从s到a期望走了多少步,答案即为f[t]。关系:f[s]=0,f[a]枚举所有a的前驱,乘以转移到a的概率并求和。

    • 做法2:设f[a]为从a到t期望走了多少步,答案即为f[s]。关系:f[t]=0,f[a]枚举所有a的后继,乘以a向后转移的概率并求和。

    第一种符合“走路”的思考方式,但是实际仔细思考一下大有问题:

    啥叫从起点到i的期望步数?

    从起点到i又不是一个只发生一次的事件,甚至从起点到起点自己的期望步数都不能想当然地设成0。

    那如果设成“第一次到达”呢?那转移又说不通了,融合在点a,b中的“第一次到达的步数期望”有一部分是在历史上已经到达过c了,自然不可以。

    所以整个解释都是浆糊的。

    所以用做法2

    [f [t] = 0 ]

    [f[x] = 1 + sum_{x -> y}{ f[y]p(x, y) } ]

    我们解决了列方程的问题,接下来,

    如果原图是DAG,方程就是可以直接在DAG上拓扑序DP递推出来;

    如果原图不是DAG,比如是个有环的有向图,或者甚至是个无向图,那就需要实打实的解这个方程了,用高斯消元。

    求经过每个点的期望次数

    这个和上一种问题完全不一样了。

    想知道点x的经过的期望次数,知道x的后继的经过期望次数没有用了,有用的是知道x的前驱的经过期望次数。

    所以这个是从起点开始的。

    注意这个方向和上一问是反的,枚举的是前驱不是后继。

    [f[s] = 1 + sum_{a->s}{f[a]*p(a,s)} ]

    [f[x] = sum_{a->s}{f[a]*p(a,x)} ]

    多个结束节点,问在每个结束节点结束的概率

    我们可以写出概率转移矩阵,

    将每个时刻的概率分布转移到下⼀时刻的概率分布,然后求它的⽆穷次⽅。

    更好的做法是转化成之前的“期望次数”的问题。

    我们转而求解这样一个问题:

    从某个起点出发,到每个结束节点时就会停下来

    问最终停下来以后经过每个点的期望次数

    由于结束节点只会被经过一次,所以它的经过期望次数就是最后在它那里结束的概率

    所以使用上一节的解法即可,

    注意枚举的时候把所有的从终点出发的边都删了。

    例题

    难度单调不降排序。

    P4316 绿豆蛙的归宿

    随机游走的模板题,与随机游走的第一种题相同。注意,要从终点倒着dp。

    将另一篇博客搬了过来。

    题意:
    给一张有源汇(暂且这么叫)的DAG,边有边权。
    每个出度为d的节点有1/d的概率走向其任意一条出边。
    求源点到汇点的期望路径长。
    n <= 1e5, m <= 2e5
    

    如果按正图建的话,是不对的。

    我们以建反图的思路来做这道题。

    定义f[i]为i->n的期望路径长,这样有(正图中)f[u] = Σ(u->v) ( (f[v] + len(u->v)) * P(u->v) )

    让后利用反图拓扑序解决此问题。

    补充:为什么正推不对?

    要非得用正推,只好f[i]为1->i的期望。正反对比如下图(左逆推右顺推)(最下面的字母不用管):

    绿豆蛙

    这样,我们不得不记录起点到当前点i的概率P[i],不是那么好做。(但好像也可做)。

    具体可见:题解 P4316 【绿豆蛙的归宿】

    //p,d初始时都是出度
    while (front < rear) {
    	cur = que[++front];
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		to = e[i].to;
    		f[to] += 1.0 * (f[cur] + e[i].val) / p[to];
    		if (!(--d[to])) que[++rear] = to;
    	}
    }
    

    P4206 [NOI2005]聪聪与可可

    这道题看起来似乎很不可做的样子,有两个点在无向图上走,和三种模型都不太一样。然而我们发现:

    1. 两个点只有一个点在随机游走,另一个点的行走路线在该点固定的情况下是一定的。

    2. 由于猫的走法绝对最优,并且可以连走两次,所以不会出现一直走不完的情况,也不会出现状态的环。

    于是,我们将状态设为f[u][v],表示猫在u,老鼠在v的期望步数。然后预处理出dis数组和nxt数组,记忆化搜索即可。

    Code:

    double dfs(int u, int v) {
    	if (f[u][v])	return f[u][v];
    	if (u == v)	return 0;
    	if (dis[u][v] <= 2)	return f[u][v] = 1;
    	int nw = nxt[u][v];
    	nw = nxt[nw][v];
    	int to;
    	for (register int i = head[v]; i; i = e[i].nxt) {
    		to = e[i].to;
    		f[u][v] += (dfs(nw, to) + 1) / (d[v] + 1);
    	}
    	f[u][v] += (dfs(nw, v) + 1) / (d[v] + 1);
    	return f[u][v];
    }
    

    P3232 [HNOI2013]游走

    • 问题转化:给一个无向连通图,1 -> n,等概率游走。求每一条边的期望经过次数g[i]。

    • 解析:

    可转化成求点的期望经过次数f[i]。

    (d为度数)

    [g[i] = frac{f[u]}{d[u]} + frac{f[v]}{d[v]} ]

    不是DAG,需要高斯消元

    注意n是终点,不要算它的f。

    (Code):

    inline void gx() {//高斯消元
    	for (register int i = 1; i <= n; ++i) {
    		int mx = i;
    		for (register int j = i + 1; j <= n; ++j)
    			if (F(a[j][i]) > F(a[mx][i]))	mx = j;
    		if (F(a[mx][i]) <= eps)	continue;
    		for (register int j = 1; j <= n; ++j) {
    			if (j == mx)	continue;
    			double k = a[j][i] / a[mx][i];
    			for (register int p = 1; p <= n + 1; ++p)
    				a[j][p] -= a[mx][p] * k;
    		}
    		for (register int p = 1; p <= n + 1; ++p)
    			swap(a[mx][p], a[i][p]);
    	}
    	for (register int i = 1; i <= n; ++i)
    		f[i] = a[i][n + 1] / a[i][i];
    }
    
    ...
    
    for (register int cur = 1; cur < n; ++cur) {
    	a[cur][cur] = 1;
    	register int to;
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		to = e[i].to;
    		if (to == n)	continue;
    		a[cur][to] = -1.0 / d[to];
    	}
    }
    a[1][n + 1] = 1;
    gx();
    for (register int i = 1; i <= ecnt; i += 2) {
    	if (e[i].from != n)	g[i/2] += f[e[i].from] / d[e[i].from];
    	if (e[i].to != n)	g[i/2] += f[e[i].to] / d[e[i].to];
    }
    

    CF446D DZY Loves Games

    首先分层图,掉血即为进入下一层。目标:((1,K)) -> ((n, 1)) 的概率。

    然后就成了“从一个点出发,最终到另一个点的概率”。(O((nK)^3))即可完成此题。

    发现分层图可以分层高消,(O(n^3K))即可完成此题。

    然而 (1e9)(K) 总是那么烦人。发现每层都是相似的,因此可以直接高消记录一下 (i -> j) 的概率。然而 (500) 限制了 (n^3log),并且如果 (j) 不是掉血点,就难以用“期望经过次数”来代替“概率”了。因此,考虑“缩点”,即计算出关键点到关键点的概率。注意到以不同的点作为起点,方程只是在系数上有变化,因此可以用高消的时候带着系数一起消的方法来迅速求出。这种方法类似矩阵的逆。

    最后要注意:这里不能理所当然的设置起点的方程为:

    [f[S] = 1 + sum_{from}{f[from] * p[...]} ]

    因为我们要求的是从 S 出来以后在经过 (S) 的期望次数,不能上来给个1.因此需要把这个1分到与之连边的那些点上。

    其他例题:

    bzoj1444 [Jsoi2009]有趣的游戏

    TC SRM 641 BitToggler


    almost all by gzz

    还要多做题啊。

  • 相关阅读:
    linux recv 返回值与linux socket 错误分析
    位域
    mysql修改root密码的方法
    mysql Plugin ‘InnoDB’ init function returned error
    centos查看版本
    CentOS 7.0 使用 yum 安装 MariaDB 及 简单配置
    CentOS 7.X 中systemctl命令用法详解
    phpMyAdmin关于PHP 5.5+ is required. Currently installed version is: 5.4.16问题
    linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结
    自动设置IP地址bat脚本
  • 原文地址:https://www.cnblogs.com/JiaZP/p/14438261.html
Copyright © 2011-2022 走看看