这类题我觉得难度与数论题相当,因为期望概率本质上也是一个数学推理的过程,所以有些题做起来可谓是十分自闭。
洛谷P2473
首先这道题目很有意思,它题目要求求出期望值,且可不可选此物与之前选过什么物品有关,所以状态可以记录成前面已经选过什么物品。
然后这题的精髓之处就是要倒着推导状态,为什么要倒着推呢?刚刚讲了可不可选此物与之前选过什么物品有关,如果我们正着推的话,当前所选的物品会受到之前所选的物品的牵连,怎么个牵连法?
举个例子,正着推我们推到第2个数据的时候,发现我们要先选后面的第3个物品才能选到,正着推显然不能符合逻辑,还可能推出一些不可能的状态,因为你不知道当前枚举的状态是否合理。
那倒着推呢?我们就假设所有的物品都已经选好,而且我们终归是要回归到什么物品都没有选的状态,所以倒着推,我们就相当于把当前物品给删除,前一个删除与后一个删除没有关系因为即使两者是有先后选择关系的,倒着推,终归能回到同一个状态。比如当前 11110001这个状态,它可以由11111111-1110得到,亦可以11111001-1000.
#include <bits/stdc++.h> using namespace std; int i,x,f[505],j,a[505],K,n,k; double dp[105][1<<15]; int main() { cin>>K>>n; for (i=1;i<=n;i++) { scanf("%d",&a[i]); while (1) { scanf("%d",&x); if (x==0) break; f[i]+=(1<<(x-1)); } } for (i=K;i>=1;i--) for (j=0;j<(1<<n);j++) { for (k=1;k<=n;k++) { if ((j&f[k])==f[k]) dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<(k-1))]+a[k]); else dp[i][j]+=dp[i+1][j]; } dp[i][j]/=n; } printf("%.6lf",dp[1][0]); return 0; }
洛谷P4316
这道题目用到了一个叫做期望可加性的东西,也就是E(X+Y)=E(X)+E(Y)
由于题目说明了肯定能走到终点,意思即是条条大路通罗马,所以对于每条路径,它都可能会走到,即每条边都会有贡献。
那么就是E(sum)=E(第一条边)+E(第二条边)+E(第三条边).......
对于某一条边,它的期望值是这么算的,E(某一条边)=边权×走这条边的概率。概率很容易求出来,那么这题也就写完了。
#include <bits/stdc++.h> #define mp make_pair #define maxn 100005 using namespace std; vector <pair<int,int> > maap[maxn]; queue <int> q; double ans,probability[maxn]; int in[maxn],out[maxn],i,n,m,x,y,v; void bfs() { int u,v,value; q.push(1); while (!q.empty()) { u=q.front(); for (auto now:maap[u]) { v=now.first; value=now.second; ans+=probability[u]/(out[u]*1.0)*value; probability[v]+=probability[u]/(out[u]*1.0); in[v]--; if (in[v]==0) { q.push(v); } } q.pop(); } } int main() { cin>>n>>m; for (i=1;i<=m;i++) { cin>>x>>y>>v; maap[x].push_back(mp(y,v)); out[x]++;in[y]++; } probability[1]=1; bfs(); printf("%.2lf",ans); return 0; }
CF280C
跟上一题一样的性质,期望可加性。
E(染一棵树)=E(把第一个点染黑)+E(把第二个点染黑)+......
E(某个点 i )=步数×概率,染某个点的步数为1,那概率呢?
这里并不是1/n的概率,而是1/depth【i】,因为能把这个点染黑的只有它所有的父节点做的到,所以我们只把这一个点染黑的概率就为1/depth【i】
#include <bits/stdc++.h> using namespace std; double ans; int n,x,y,i,depth[100005]; vector <int> q[100005]; void dfs(int x,int father) { depth[x]=depth[father]+1; for (auto v:q[x]) { if (v==father) continue; dfs(v,x); } } int main() { cin>>n; for (i=1;i<n;i++) { cin>>x>>y; q[x].push_back(y); q[y].push_back(x); } dfs(1,0); for (i=1;i<=n;i++) { ans+=1/(depth[i]*1.0); } printf("%.20lf",ans); return 0; }
牛客LOJ6162
E(总答案)=E(第一个人的贡献)+E(第二个人的贡献)+......
首先我们要知道每个人在每个位置的概率一定是1/n,然后我们来求某个人的期望
E(第某人的贡献)=E(某人在第一个位置的贡献)+E(某人在第二个位置的贡献)+......
那么比如某人在第j个位置,所以他的速度就是题目给的c[i]-(j-1)*d[i] 米/秒,我们要知道这个人是从队尾才开始追上去的,所以路程为u×n,相对速度就是c[i]-(j-1)*d[i] - v(这个v题目一开始就给出了),所以时间就是路程/相对速度,再乘上在第某个位置的概率1/n,就等于答案了。
#include <bits/stdc++.h> #define maxn 1005 using namespace std; int i,j,n; double ans,c[maxn],d[maxn],v,u; int main() { cin>>n>>v>>u; for (i=1;i<=n;i++) cin>>c[i]; for (i=1;i<=n;i++) cin>>d[i]; for (i=1;i<=n;i++) for (j=1;j<=n;j++) ans+=(u*n/(c[i]-v-(j-1)*d[i]))/n; printf("%.3lf",ans); return 0; }
牛客14962
这题用了个叫做赌徒破产模型,我看不懂,也有人说是随机游走模型http://www2.math.uu.se/~sea/kurser/stokprocmn1/slumpvandring_eng.pdf有系统介绍,还是看不懂,但是我觉得随机游走模型适用性会更广。
然后听说有个模板可以解决这类问题。
#include <bits/stdc++.h> using namespace std; double ans,val[200005]; int n,l,r,m,L,R,i,j,A,B; double cal(double p,int n,int m)//模板 { val[n]=1; for (int i=n+1;i<=m;i++) val[i]=(val[i-1]-(1.0-p)*val[i-2])/p; return val[n]/val[m]; } int main() { cin>>n>>l>>r; cin>>m>>L>>R; for (i=l;i<=r;i++) for (j=L;j<=R;j++) { if (i>j) A++; if (i<j) B++; } double p=1.0*A/(A+B); if (p>0.5) printf("%.5lf",cal(p,n,n+m)); else printf("%.5lf",1.0-cal(1-p,m,n+m)); // printf("%.5lf",cal(p,n,n+m)); return 0; }