[题解] [笔记]期望&洛谷P3232
[题解] [笔记]期望&洛谷P3232
期望
概念
期望在信息学中指的一般是达到结果的期望,最简单的计算方法就是每个情况的概率乘以这个情况的结果的值
例题理解以及公式
问题1
持续地抛一枚硬币,直到连续两次硬币正面朝上,求期望要抛的次数
解答:设\(f_0\)为没有抛出过正面的期望次数,\(f_1\)为已经抛出一次正面还要再抛一个正面的期望次数,则可以得到
问题2
你面前有\(n\)扇门,每道门有一个战力要求\(c_i\)和时间\(t_i\)同时你有一个战斗力\(v\)。每一轮你都会随机挑选一扇门进行挑战:若\(v>c_i\)则会花费\(t_i\)的时间离开;否则战力提升\(c_i\)。求期望多少轮后能离开?\(n ≤ 100, v, c_i ≤ 10^4\)
解答:设\(f_i\)代表战力为\(i\)时期望的逃离时间,则可以知道:
问题3
买彩票。一张彩票\(2\)元,每卖出\(1000000\)张就开奖,中奖号码是\(342356\),每张彩票也是一个\(6\)位数的数字,奖励规则如下:
-
最后一位相等奖励\(4\)元,中奖概率\(0.1\%\)
-
后两位相等奖励\(20\)元,中奖概率\(0.01\%\)
-
后三位相等奖励\(200\)元,中奖概率\(0.001\%\)
-
后四位相等奖励\(2000\)元,中奖概率\(0.0001\%\)
-
后五位相等奖励\(20000\)元,中奖概率\(0.00001\%\)
求彩票公司每卖出一张彩票的期望收益。
解答:通过上面两道比较简单的例题立即期望的概念,我们知道算期望就是把每种情况的权值与它对应的概率相乘。那么来看这道题。先计算公司的期望支出:\(0.1*4+0.01*20+0.001*2000+0.00001*20000=1.2\),也就意味着每卖出一张彩票公司的期望支出为\(1.2\)元,而彩票的单价是\(2\)元,所以公司每卖出一张彩票的期望收益就是\(0.8\)元。
公式
期望用\(E\)表示,概率用\(P\)表示,则有:\(E(x)=X_1*P(X_1)+X_2*P(X_2)+...+X_n*P(X_n)\)
题解
有了对期望的大致了解就可以来看洛谷的这道题了。
思路分析
根据贪心的原则,为了使答案尽可能的小,那么期望经过次数多的边的编号就要尽可能的小。那么问题就变成了求每条边的期望经过次数,那么再想一条边被经过肯定是由它的两个端点来的,那么就可以表示一下:\(g_e\)表示经过边\(e\)的期望次数,\(f\)表示点的期望经过次数,\(d\)表示点的入度,就\(g_e=\frac{f_u}{d_u}+\frac{f_v}{d_v}\),因为每次到达一个点\(u\)的时候都有\(\frac{1}{d_u}\)的概率经过边\(e\)。那么问题又转化成求经过点的期望次数:\(f_u=\sum_{v\in E_u}\frac{f_v}{d_v} \ \ {(E_u)表示的是和u相连的点集}\),但要注意\(1\)号节点不一样,\(f_1=1+\sum_{v\in E_u}\frac{f_v}{d_v}\),因为一开始就走过了,而\(n\)号节点是目标节点,走到了就不能再到别的点了所以\(f_n=0\)。
接着就是求解了,我们可以把上面\(f\)的式子看作\(n-1\)个方程,用高斯消元求解就行。但这里既然是用高斯消元求解就要表示成格式统一的方程式,方法如下:
代码
#include <bits/stdc++.h>
using namespace std;
struct node{
int to,nxt;
}e[125010 * 2];
int fir[510],tot,d[510];
int u[125010],v[125010];
void add(int x,int y){
e[++tot].to = y;
e[tot].nxt = fir[x];
d[x]++;
fir[x] = tot;
}
int n,m;
double eps = 1e-9;
double f[510][510],g[125010];
void gauss(){
for(int i = 1;i <= n;i++){
int now = i;
for(int j = i + 1;j <= n;j++){
if(fabs(f[j][i]) > fabs(f[now][i]))
swap(now,j);
}
if(now != i)
for(int j = 1;j <= n + 1;j++)
swap(f[i][j],f[now][j]);
if(fabs(f[i][i]) < eps)continue;
for(int j = 1;j <= n;j++){
if(j != i){
double tmp = f[j][i] / f[i][i];
for(int k = i + 1;k <= n + 1;k++){
f[j][k] -= f[i][k] * tmp;
}
}
}
}
for(int i = 1;i <= n;i++){
f[i][n + 1] /= f[i][i];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++){
scanf("%d%d",&u[i],&v[i]);
add(u[i],v[i]);add(v[i],u[i]);
}
n--;//n号点不能转移
f[1][n + 1] = -1.0;//模拟移项的过程
for(int i = 1;i <= n;i++){
f[i][i] = -1;
for(int j = fir[i];j;j = e[j].nxt){
if(e[j].to != n + 1)
f[i][e[j].to] = 1.0 / d[e[j].to];
}
}
gauss();
for(int i = 1;i <= m;i++){
g[i] = f[u[i]][n + 1] / d[u[i]] + f[v[i]][n + 1] / d[v[i]];
}
sort(g + 1,g + m + 1);
double ans = 0.0;
for(int i = 1;i <= m;i++){
ans += g[i] * (m - i + 1);
}
printf("%.3lf\n",ans);
return 0;
}