浅谈期望的线性性(可加性)【CodeForces280c】【bzoj3036】【bzoj3143】
来源:互联网 发布:linux修改主机名 编辑:程序博客网 时间:2024/06/12 17:58
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=63399955
向大(hei)佬(e)势力学(di)习(tou)
之前一直都没有接触过期望,更别说期望dp了。
先从期望说起吧,dp什么的先不谈。浅谈一下期望的线性性(可加性),这是一个很重要的性质,主要用我做的这几道例题来更感性的理解(真的是浅谈。。。orz)
首先,期望是指一个事件有多种结果,每一种结果出现有一定的可能性。
对于随机变量x,它的期望E(x)=sigma{基本结果i发生的概率*发生基本结果i时的x的数值,i是一个基本结果}
然后是期望的线性性(可加性):
(感性理解一下)
E(X+Y)=E(X)+E(Y)
即两个(或多个)随机变量的和的期望等于期望的和
下面就由题目来理解理解吧(题才是重点):
Codeforces280c
题目大意
给出一棵含n个白点的有根树,每次随机选择一个还没有被染黑的节点,将这个节点和这个节点子树中的所有点染黑.
问期望操作多少次后所有点都被染黑.
N<=100000
整棵树的期望操作次数太大,难以找到方法。这时我们需要突破口。
该如何将大问题转化为小问题呢?我们发现,一棵树是可以分成好几颗子树的,而子树分解的最终状态就是所有的点。那么,我们是不是可以计算出 每个点被染黑的期望操作次数,然后相加就是整棵树的了?答案是当然可以。
这里需要注意的是,对于每个点的操作次数是指的在这个点上的操作。对于每一个点,如果其祖先被染黑了,它自己也会被顺带染黑,而这个对于该点来说是没有进行操作的。所以得出对于点x:E(x)=1/dep[x]
dfs就可以了
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=100000+5;int n;int head[N],end[N*2],nxt[N*2],hh=0;double ans=0;void dfs(int u,int f,int dep){ ans+=1.0/dep; for(int i=head[u];i;i=nxt[i]){ int v=end[i]; if(v==f) continue; dfs(v,u,dep+1); }}void adde(int a,int b){ hh++; end[hh]=b; nxt[hh]=head[a]; head[a]=hh;}int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ int a,b; scanf("%d%d",&a,&b); adde(a,b),adde(b,a); } dfs(1,1,1); printf("%.20lf",ans); return 0;}
bzoj3036绿豆蛙的归宿
Description
随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿。
给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度。绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K 。
现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?
Input
第一行: 两个整数 N M,代表图中有N个点、M条边
第二行到第 1+M 行: 每行3个整数 a b c,代表从a到b有一条长度为c的有向边
Output
从起点到终点路径总长度的期望值,四舍五入保留两位小数。
Sample Input
4 4
1 2 1
1 3 2
2 3 3
3 4 4
Sample Output
7.00
HINT
对于100%的数据 N<=100000,M<=2*N
很显然,由期望的线性性可得:经过路径期望总长度=sigma{每条边期望经过次数*边权}
因为是有向无环图,所以 每条边的期望经过次数=该边起点的期望经过次数*从该起点出发经过该路径的概率。
于是问题转成了求每个点的期望经过次数。很显然,每个点的期望经过次数=sigma{入边i的期望经过次数}
于是发现边与点的期望值是相辅相成的关系,由于是有向无环图,所以拓扑排序就可以了
#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=100000+5;int n,m,in[N],out[N];int head[N],end[N*2],len[N*2],nxt[N*2],hh=0;queue<int> q;double p[N],ans=0;void adde(int a,int b,int c){ hh++; end[hh]=b; len[hh]=c; nxt[hh]=head[a]; head[a]=hh;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); adde(a,b,c); in[b]++,out[a]++; } q.push(1); p[1]=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=nxt[i]){ int v=end[i]; ans+=p[u]/out[u]*len[i]; p[v]+=p[u]/out[u]; in[v]--; if(in[v]==0){ q.push(v); } } } printf("%.2lf",ans); return 0;}
bzoj3143[Hnoi2013]游走
Description
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
Input
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。
Output
仅包含一个实数,表示最小的期望值,保留3位小数。
Sample Input
3 3
2 3
1 2
1 3
Sample Output
3.333
其实这道题与绿豆蛙有相似之处,但是图变为了无向连通图。
首先明确一个简单的贪心:我们希望期望经过次数最大的边编号最小。然后就是如何求期望经过次数了。原理与上一题一样,由点推边。
对于一号点,f[1]=1+sigma(f[j]/degree(j) , j和1有边)
对于其他点,f[i]=sigma(f[j]/degree(j),j和i有边)
但由于是无向图,一个点可以被经过数次,还有极小的的可能在一条边上来回走动。于是我们发现这个图是有后效性的,每个点的值会互相影响,所以不能递推。wa,那可怎么办啊?
大佬告诉我,像这种会互相影响的值,其实和方程组很像,高斯消元即可。woo,学到了。
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=500+5;const double eps=1e-10;int n,m;int deg[N],edge[N*N][2];int head[N],end[N*N*2],nxt[N*N*2],hh=0;double g[N][N],p[N],ep[N*N];void adde(int a,int b){ hh++; end[hh]=b; nxt[hh]=head[a]; head[a]=hh;}void solve(){ int cnt=0; for(int k=1;k<n;k++){ int j=-1; for(int i=cnt+1;i<n;i++){ if(fabs(g[i][k])>eps){ j=i; break; } } if(j==-1) continue; cnt++; for(int i=1;i<=n;i++) swap(g[cnt][i],g[j][i]); for(int i=1;i<n;i++){ if(i==cnt) continue; if(fabs(g[i][k])<eps) continue; double tmp=g[i][k]/g[cnt][k]; for(j=1;j<=n;j++) g[i][j]-=g[cnt][j]*tmp; } } for(int i=1;i<n;i++){ p[i]=g[i][n]/g[i][i]; }}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int a,b; scanf("%d%d",&a,&b); adde(a,b),adde(b,a); deg[a]++,deg[b]++; edge[i][0]=a,edge[i][1]=b; } for(int i=head[1];i;i=nxt[i]){ int v=end[i]; if(v==n) continue; g[1][v]=1.0/deg[v]; } g[1][1]=-1; g[1][n]=-1; for(int k=2;k<n;k++){ g[k][k]=-1; for(int i=head[k];i;i=nxt[i]){ int v=end[i]; if(v==n) continue; g[k][v]=1.0/deg[v]; } } solve(); for(int i=1;i<=m;i++){ int a=edge[i][0],b=edge[i][1]; ep[i]=p[a]/deg[a]+p[b]/deg[b]; } sort(ep+1,ep+m+1); double ans=0; for(int i=1;i<=m;i++){ ans+=ep[i]*(m-i+1); } printf("%.3lf",ans); return 0;}
总结:
1、分解成小的、可计算的期望问题,来解决大的、不可计算的期望问题
2、无后效性的可直接递推。有后效性的可解方程。
- 浅谈期望的线性性(可加性)【CodeForces280c】【bzoj3036】【bzoj3143】
- [BZOJ3036]绿豆蛙的归宿(拓扑序+期望dp)
- [BZOJ3036][codevs2488]绿豆蛙的归宿(期望dp)
- [bzoj3036]绿豆蛙的归宿 期望DP
- 【bzoj3036】绿豆蛙的归宿 期望dp
- [BZOJ3143][Hnoi2013]游走(概率期望+高斯消元)
- [BZOJ3143][HNOI2013]游走(高斯消元解期望方程)
- bzoj4318 OSU!(期望概率DP,期望的线性性)
- BZOJ3036 期望DP
- BZOJ3143 游走 [期望][高斯消元]
- [BZOJ3036]绿豆蛙的归宿 ——期望DP
- BZOJ3036-绿豆蛙的归宿-概率与期望-DP
- BZOJ3036[绿豆蛙的归宿] 期望概率DP
- CodeForces 280C 浅谈期望线性性的树上问题实际运用
- BZOJ 2720 浅谈期望线性性分部转移
- 关于期望线性性的探讨
- 【BZOJ3143】【Hnoi2013】游走 高斯消元 解期望方程
- 【BZOJ3143】【Hnoi2013】 游走 期望DP 高斯消元
- Java 浮点型与双精度数值比较
- 解决win10 Ipv6无法连接的问题
- 23种设计模式(6)-装饰者模式
- [置顶]【Linux init】systemd 服务单元管理
- JTAG
- 浅谈期望的线性性(可加性)【CodeForces280c】【bzoj3036】【bzoj3143】
- 99%的人都理解错了HTTP中GET与POST的区别
- C++中有符号类型(int)和无符号类型(unsigned int)混合算术运算的类型转换会出现的问题
- 操作系统常考面试题
- 权值线段树小结
- Java创建多线程的三种方法
- RecyclerView上下拉刷新
- Decode rpc invocation failed: expected map/object at java.lang.String (Ljava/lang/String;)
- redis数据结构详解之Hash(四)