概率期望
概率期望
引:
一个简单问题:
在一条数轴上, 从0点开始, 每一秒有一般的几率向正方向走一个单位, 有一半几率停着不动
问在\(0,1\)的概率与期望秒数
如果是停留在\(0\), 可以$f[0] = (f[0] + 1)*0.5 \(, 所以\)f[0]=1$
也可以\(\sum^{\infty}(\frac{1}{2})^i\)
但是发现算\(1\)的时候, 在\(n\)秒中, 可以随意选择其中一秒走一步, 其他停住, 这样答案算起会很大(\(\sum^{\infty}(\frac{1}{2})^i*i^2\))
显然错了, 因为到达自己之后仍然可以停留, 那么这个事件的定义就不明确了(既不是停留, 又不是到达)
如果是定义第一次到这个点的话, 就不能算自己到自己的这个事件, 这种傻逼问题之后可能又会碰到
基本定义
- 概率空间
- \(\Omega\)
- 基本事件
- (elementray event, 单元事件, 不可再分): \(\omega\)
- 事件
- (event): \(A\)
\(\omega \in \Omega, \omega \in A, A \subseteq \Omega\)
- 随机变量
- (定义在\(\omega\)上): \(S\), 如\(S(\omega)\)定义为两个骰子点数和, 再例如"\(事件S(\omega)=7的概率\)"
- 独立的随机变量
- 如果同一个\(\Omega\)中的两个随机变量\(X,Y\), 有
则称\(X,Y\)为独立的随机变量(这是定义)
套路
考虑每一步对期望的贡献
有 \(n\) 个点, 一开始全白, 每次某个概率选一个点染成黑色, 求期望全染黑的步数?
有期望的定义式可得: \(Ans = \sum_{i > 0} i \cdot Pr_{第 i 步恰好全黑}\)
考虑什么时候会走这个 第$i步 , 即第 \(i\) 步时未全染黑的概率, 因为只要这时未全染黑, 后面无论何时染黑(后面情况加起来概率为1), 都需要走这一步, 所以这一步的贡献 = 概率
套路: 上述期望 = \(\sum_{i > 0} Pr_{第 i 步没有全黑的概率}\)
简单题
LightOJ1027 A Dangerous Maze <简单>
假迷宫, 假如门的代价是正数, 就可以花这个代价出去, 否则要花绝对值的代价, 留在原地
问出去的期望
\(E = \sum \frac{1}{n}*cost_i+\sum \frac{1}{n}*(cost_i + E)\)
化简即可
int T = in();
for (int t = 1; t <= T; ++ t)
{
n = in();
int sumx = 0, sumy = 0, cnty = 0, g = 0;
for (int i = 1; i <= n; ++ i)
{
int x = in();
if (x > 0) sumx += x; else sumy -= x, ++ cnty;
}
if (sumx == 0) { printf("Case %d: inf\n", t); continue; }
g = gcd(sumx + sumy, n - cnty);
printf("Case %d: %d/%d\n", t, (sumx + sumy) / g, (n - cnty) / g);
}
LightOJ1030 Discovering Gold <简单>
金矿有\(n\)个, 每个金矿有一定收益, 一开始在\(1\), 投骰子\([1,6]\), 假如走这些步数后\(>n\)就一直投直到\(\le n\), 求到\(n\)时期望收益
memset(f, 0, sizeof f);
for (int i = n - 1; i >= 1; -- i)
{
int p = min(6, n - i);
for (int j = 1; j <= p; ++ j)
f[i] += (f[i + j] + val[i + j]) / (double)p;
}
ans = f[1] + val[1];
LightOJ1038 Race to 1 Again <预处理?!>
\(T(\le 10)\)组数据, 每次给出\(N(\le 10^5)\)
每个数有相同几率跳到他的因数(包括自己), 求跳到\(1\)的期望步数
设\(cnt\)为\(i\)的因数个数, \(f[i]=\frac{1}{cnt}*\sum f[j]+1\)
\((1-\frac{1}{cnt})f[i]=\frac{1}{cnt} \sum ^{!=i}f[j]+1\)
\(f[i]=(\sum^{!=i}f[j]+cnt) / (cnt - 1)\)
所有\(10^5\)以内都可以一遍\(DP\)预处理
int T, n;
double dp[MAXN];
void init()
{
for (int i = 2; i <= 100000; ++ i)
{
double sum = 0, cnt = 0;
for (int j = 1; j * j <= i; ++ j)
{
if (i % j != 0) continue;
sum += dp[j], cnt += 1;
if (i / j != j) sum += dp[i / j], cnt += 1;
}
dp[i] = (sum + cnt) / (cnt - 1);
}
}
int main()
{
init();
T = in();
for (int i = 1; i <= T; ++ i)
{
n = in();
printf("Case %d: %.6lf\n", i, dp[n]);
}
return 0;
}
LightOJ1248 Dice (III) <期望初步>
问\(n\)面的均匀的骰子每一面都投到的期望次数
\(E_i\)表示已经有\(i\)个不同的面时的期望
那么\(E_i = 1 + \frac{i}{n}E[i]+\frac{n-i}{n}E[i+1]\)
意思是说: 现在投一次, 有\(i/n\)的几率投到出现过的, 那么又回到\(i\)这个点, 还有剩下几率投到新的面
LightOJ1104 Birthday Paradox <正难则反>
求当一年为\(n(\le 10^5)\)天时, 至少要多少人, 才能满足他们中至少有两人生日相同的概率\(\ge 0.5\)
Sol:
竟然不会....
至少有两人相同可以转化成\(1.0-\)所有人都不相同的概率
BZOJ2752: HAOI2012高速公路 <假期望>
有\(n(\le 10^5)\)个点, 相邻两点之间有边权, 初始是\(0\)
有\(M(\le 10^5)\)个询问/操作, 操作是将\([l, r]\)之间的边权都加上一个值, 查询是问在\([l,r]\)中选两个不同的点, 他们之间边权和的期望
Sol:
假期望, 这是所有选择的边权和的和, 让它取除以方案数\(\tbinom{r-l+1}{2}\)
中等
LightOJ1151 Snakes and Ladders <高斯消元>
自以为思路很正确, 实则漏洞百出
在\(1*100\)的棋盘上, 每个点至多有一个单向传送, 起点和终点不会有传送
可以扔一个\(6\)面的骰子, 假设投到\(x\), 然后花一步跳到往后数\(x\)个格子上, 如果超过\(100\)就重新投(但是要花费步数)
问期望几次从\(1\)到达\(100\)
Sol:
因为传送是xjb乱传的, 不能递推, 用高斯消元
考虑期望方程(读清题意!!!)
能传送和不能传送的点是不同的, 能传送的点根本就不能有任何操作直接传送到那个点!!!
能传送:
不能传送:
可以根据这些列出方程
int n, to[MAXN];
DB a[MAXN][MAXN];
DB ansx[MAXN];
void check(int n)
{
puts("------");
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= n + 1; ++ j)
printf("%.1f ", a[i][j]);
puts("");
}
puts("------");
}
bool nosol, mulsol;
void Gauss(int n)
{
int r = 1, c = 1, maxr = 0;
for (; r <= n && c <= n; ++ r, ++ c)
{
maxr = r;
for (int i = r + 1; i <= n; ++ i)
if (fabs(a[i][c]) - fabs(a[maxr][c]) > EPS) maxr = i;
if (maxr != r)
for (int i = 1; i <= n + 1; ++ i) // n + 1 !!!
swap(a[r][i], a[maxr][i]);
if (fabs(a[r][c]) < EPS) { -- r; continue; }
for (int i = r + 1; i <= n; ++ i)
{
double k = a[i][c] / a[r][c];
for (int j = c; j <= n + 1; ++ j)
a[i][j] = a[i][j] - k * a[r][j];
}
}
-- r, -- c;
for (int i = n; i >= 1; -- i)
{
int all0 = 1;
for (int j = i; j <= n; ++ j) all0 &= (fabs(a[i][j]) < EPS);
if (all0)
{
if (fabs(a[i][n + 1]) > EPS) nosol = true;
else mulsol = true;
}
else
{
for (int j = i + 1; j <= n; ++ j)
a[i][n + 1] -= ansx[j] * a[i][j];
ansx[i] = a[i][n + 1] / a[i][i];
}
}
}
int main()
{
int T = in();
for (int t = 1; t <= T; ++ t)
{
memset(ansx, 0, sizeof ansx);
memset(a, 0, sizeof a);
memset(to, 0, sizeof to);
n = in();
for (int i = 1; i <= n; ++ i)
{
int x = in(), y = in();
to[x] = y;
a[x][x] = 1; a[x][y] = -1;
}
for (int i = 1; i <= 100 - 1; ++ i)
{
if (to[i]) continue;
a[i][i] = min(6, 100 - i);
for (int x = 1; x <= min(6, 100 - i); ++ x)
a[i][i + x] = -1;
a[i][101] = 6;
}
a[100][100] = 1;
Gauss(100);
printf("Case %d: %.7f\n", t, ansx[1]);
}
return 0;
}
LightOJ 1079 Just another Robbery <正难则反>
\(100\) 组数据, 每组\(\le 100\)个银行, 每个银行有一个被抓住的几率, 和成功时获得的收益(\(\le 100\))
问当总的被抓的几率\(\le lim\)时能获得的最大收益
其实算没被抓的几率就可以了
也可以像我一样傻逼的算被抓的几率:
- 证明以不同顺序抢同一些银行被抓的几率相同
- 列一下式子, 发现是容斥, 证毕
- 证明抢一组银行的被抓/没被抓的几率可以整合, 视作一个合体银行
- 被抓=\(a+(1-a)*b+(1-a)*(1-b)*c=P_{ab}+(1-P_{ab})*c\)
- 展开发现一样, 也是容斥, 或者感性理解也行
好了, 滚动(一开始忘记)背包, 下标是收益
for (int k = 1; k <= T; ++ k)
{
sum = ans = tot = 0;
scanf("%lf%d", &lim, &n);
for (int i = 0; i <= 10000; ++ i) f[i] = 1; f[0] = 0;
for (int i = 1; i <= n; ++ i)
{
int x; double y;
scanf("%d%lf", &x, &y);
if (y <= lim)
{
++ tot;
c[tot] = y, v[tot] = x;
sum += x;
}
}
for (int i = 1; i <= tot; ++ i)
for (int j = sum; j >= v[i]; -- j)
f[j] = min(f[j], f[j - v[i]] + (1 - f[j - v[i]]) * c[i]);
for (int i = sum; i >= 0; -- i)
if (f[i] <= lim) { ans = i; break; }
printf("Case %d: %d\n", k, ans);
}
XJOI20190725T3游戏 <假博弈真期望>
Alice和Bob两个人正在玩一个游戏,游戏有很多种任务,难度为p的任务(p是正整数),有1/(2p)的概率完成并得到2(p-1)分,如果完成不了,得0分。一开始每人都是0分,从Alice开始轮流做任务,她可以选择任意一个任务来做;而Bob只会做难度为1的任务。只要其中有一个人达到n分,即算作那个人胜利。求Alice采取最优策略的情况下获胜的概率。
输入格式:
一个正整数n,含义如题目所述。
输出格式:
一个数,表示Alice获胜的概率,保留6位小数。
样例输入:
1
样例输出:
0.666667
数据范围:
对于30%的数据,n≤10
对于100%的数据,n≤500
乍一看以为是博弈论
没想到是枚举所有状态进行概率\(DP\)
设\(DP[i][j]\)表示\(A, B\)分别为\(i,j\)时到达赢的状态的概率, 倒着推
那就枚举所有的任务来转移, 取其中最大的转移
把\(DP[x][y]|x\ge n ~and~y\ge n\)全部设成\(1.0\), 这样就不用考虑边界要不要乘\(B\)的概率的问题(想想为什么?)
for (int i = 1; i <= 10; ++ i)
val[i] = 1 << (i - 1), p[i] = 1 << i;
n = in();
for (int j = n; j <= 1024; ++ j)
for (int i = 0; i <= n; ++ i)
dp[j][i] = 1.0;
for (int i = n - 1; i >= 0; -- i)
{
for (int j = n - 1; j >= 0; -- j)
{
double tmp = 0.0;
for (int x = 1; x <= 10; ++ x)
{
tmp = max(tmp, ((dp[i + val[x]][j + 1] + dp[i + val[x]][j]) * 1.0 / (double)p[x] * 0.5
+ dp[i][j + 1] * (1.0 - 1.0 / (double)p[x]) * 0.5)
/ (1.0 - (1.0 - 1.0 / (double)p[x]) * 0.5));
}
dp[i][j] = tmp;
}
}
printf("%.6f\n", dp[0][0]);
难题
LightOJ - 1395 A Dangerous Maze (II) <!!!SO HARD!!!>
好题
题意概述:
你在一个迷宫里(假), 起点有\(n(\le 100)\)个门
走一个门有个代价\(x_i (1\le abs(x_i)\le10000)\), 如果是正数, 就可以花费这个代价离开迷宫, 如果是负数, 花费代价的绝对值, 仍然停留在原地
但是你只能记住最后访问的\(k(\le 100)\)个门, 你不会走这些门(因为你还待在迷宫里, 意味着那些门不能出去), 而其他门是随机选择的
问期望离开迷宫的代价
Sample Input
4
2 0
10 10
2 0
10 -10
3 1
10 -10 -20
3 2
10 -10 -20
Sample Output
Case 1: 10
Case 2: 20.000
Case 3: 30.0000000000
Case 4: 25.0000000000
Sol:
先考虑没有记忆的情况, 即每次都随机选门, 设能出去的门个数为\(x\), 不能出去的为\(y\)
则期望
那么有记忆呢?
- 首先\(k = \min(n,k)\)
假设当前记住的状态为\(now\), 记住了\(i\)个
这是候选项是\(n - i\), 不能出去的门还剩\(y-i\)
\(k=i\)时略有不同
可以发现因为这些式子加好后面枚举了剩下的门, 所以这些期望方程大概形成了树型的关系
但是每个状态不同, 剩下的门也不同, 怎么办
可以把树上同一层的式子\(E[i]\)加起来, 把\(E[i]\)作为一个整体来算
感性发现因该是
倒着推就行了, 理性证明见这里
int T, n, m;
int x, y;
double sumx, sumy;
double p[106];
int main()
{
scanf("%d", &T);
for (int tnum = 1; tnum <= T; ++ tnum)
{
x = y = 0; sumx = sumy = 0.0;
n = in(), m = in();
p[0] = 0.0;
for (int i = 1; i <= n; ++ i)
{
p[i] = 0.0;
int tmp = in();
if (tmp >= 0) ++ x, sumx += tmp;
else ++ y, sumy -= tmp;
}
if (y == 0) { printf("Case %d: %.9f\n", tnum, sumx / (double)x); continue; }
if (x == 0) { printf("Case %d: -1\n", tnum); continue; }
m = min(m, y);
p[m] = (double)(sumx + (y - m) * sumy / (double)y) / (double)(n - m) / (1.0 - (double)(y - m) / (double)(n - m)) ;
for (int i = m - 1; i >= 0; -- i)
p[i] = (sumx + (double)(y - i) * (sumy / (double)y + p[i + 1])) / (double)(n - i);
printf("Case %d: %.9f\n", tnum, p[0]);
}
return 0;
}
BZOJ1076: [SCOI2008]奖励关 <较难>
发放\(k(\le 100)\)轮宝物, 每次随机发放\(n(\le 15)\)个宝物其中一个, 即每次对于所有宝物几率都为\(\frac{1}{n}\)
宝物的价值可能为负(\(\in [-10^6, 10^6]\)), 并且拿取宝物有先决条件, 要满足当前已经有某些宝物
你可以决策收不收取这轮的宝物(不满足先决条件时一定不能收取), 求在所有可能的情况下最优决策得到的平均价值
Sol:
最有决策的平均价值是什么意思?
即对于当前这个状态, 有\(n\)种转移, 每种转移又有取与不取两种决策, 最优决策当然是取收益大的那个决策, 而平均价值则是把每个转移两个里较大的那个加起来, 再除以\(n\)
要怎么知道转移后收不收取那个更优呢? 想到了倒着推
\(f[i][sta]\)表示到第\(i\)轮时, 每个宝物是否拥有的情况为\(sta\), 到末状态的最优决策的平均价值
//BZOJ MLE返回TLE
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++ i)
{
scanf("%d", &val[i]);
while (true)
{
int x; scanf("%d", &x);
if (!x) break;
prem[i] |= (1 << (x - 1));
}
}
for (int i = m - 1; i >= 0; -- i)
{
for (int now = 0; now < (1 << n); ++ now)
{
for (int j = 1; j <= n; ++ j)
{
if ((now | prem[j]) != now) f[i][now] += f[i + 1][now];
else f[i][now] += max(f[i + 1][now | (1 << (j - 1))] + val[j], f[i + 1][now]);
}
f[i][now] /= (double)n;
}
}
printf("%.6f\n", f[0][0]);
BZOJ1426 收集邮票 <辅助期望>
神题:
有n种不同的邮票,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n,购买第k张邮票需要支付k元钱。
求得到所有种类的邮票需要花费的钱数目的期望。
input
3
output
21.25
Sol:
设\(f[i]\)为有了\(i\)张时, 期望再买几次能凑齐, \(g[i]\)为...时期望代价
\(f[i]=\frac{i}{n}f[i]+\frac{n-i}{n}f[i+1]+1\)
\(g[i]=\frac{i}{n}(g[i]+f[i]+1)+\frac{n-i}{n}(g[i+1]+f[i+1]+1)\)
即把当前花费当做\(1\), 其他一次增长, 因为反正都是花费这些数, 知道总共花费几次, 花费的顺序是无所谓的
BZOJ2830: SHOI2012 随机树 <树上子问题>
初始树上只有一个点, 定义根节点深度为\(0\)
每次操作随机选择一个叶子, 给他增加两个儿子
求有\(n\)个叶子时, 叶子深度的平均值的期望, 和树深度的期望
Sol:
乍一看以为第一问比较难, 其实他比较简单
第二问:
显然是DP, 这种题在于如何选择DP的状态, 以及选择算期望/算概率, 还有借用辅助的概率/期望, 如果定义好了DP, 那么推一下是不难的
一开始想了几种一维的DP(每次要么选择最深点, 深度++, 否则深度不变), 还想过借用算大小为\(i\)的树深度为\(j\)的叶子个数的期望值来推, 都不行
观察一下这道题, 期望的深度是实数, 而对于每一种可能他的深度是整数, 而概率是实数, 不妨计算每一个深度出现的概率
如果想到左右子树分别是一个子问题的话, 这道题目就好做了, 而不是把所有叶子节点根据深度分类, 这样并不能简化问题
\(f[i][j]\)表示叶子有\(i\)个时, 深度是\(j\)的概率, 那么枚举左右字数叶子个数以及深度, 再加一个前缀和优化即可
double g[MAXN];
double f[MAXN][MAXN], p[MAXN][MAXN], t[MAXN][MAXN];
double solve(int n)
{
p[1][1] = 1;
for (int i = 3; i <= n; ++ i)
{
for (int j = 1; j < i; ++ j)
{
if (j > 1) p[j][i - j] += p[j - 1][i - j] * (DB)(j - 1);
if (i - j > 1) p[j][i - j] += p[j][i - j - 1] * (DB)(i - j - 1);
p[j][i - j] /= (double)(i - 1);
}
}
f[1][0] = t[1][0] = 1.0; f[2][1] = 1.0;
for (int i = 1; i <= n; ++ i) t[1][i] = t[2][i] = 1.0;
for (int i = 3; i <= n; ++ i)
{
for (int j = 1; j < i; ++ j)
{
for (int ls = 1; ls < i; ++ ls)
{
if (j > 1)
f[i][j] += (f[ls][j - 1] * t[i - ls][j - 2] + f[i - ls][j - 1] * t[ls][j - 2]) * p[ls][i - ls];
f[i][j] += f[ls][j - 1] * f[i - ls][j - 1] * p[ls][i - ls];
}
t[i][j] = t[i][j - 1] + f[i][j];
}
for (int j = i; j <= n; ++ j) t[i][j] = t[i][j - 1] + f[i][j];
}
double ret = 0;
for (int i = 1; i < n; ++ i) ret += 1.0 * i * f[n][i];
return ret;
}
int main()
{
m = in(); n = in();
for (int i = 2; i <= n; ++ i) g[i] = g[i - 1] + 2 / (1.0 * i);
if (m == 1) printf("%.6f\n", g[n]);
else printf("%.6f\n", solve(n));
return 0;
}
Codeforces 235B Let's Play Osu!
有长度为\(n\)的\(01\)串, 第\(i\)个位置有\(pi\)的几率是\(1\), 否则为\(0\)
一个串的值为每一段最长连续的\(1\)的长度的平方的和
求期望值
待
20191002 Cyanic Contest Problem C. Fibonacci’s Nightmare <推式子>
定义一个随机线性递推序列(即random linear recursive sequence, RLRS)是一个由如下方式生成的非负整数序列。一开始,令\(a_0= 1\)。然后对于从\(1\)开始的每一个\(n\),独立且等概率在\([0, n−1]\)中随机\(k\)个非负整数\(i_1, i_2,···, i_k,\)令\(a_n=∑^k_{j=1}a_{i_j}\)。接下来是一个例子。令\(k= 2\),那么有\(a_1=a_0+a_0= 2\),之后,\(a_2\)的值等概率从\(a_0+a_0, a_0+a_1, a_1+a_0, a_1+a_1\)中选择。小C解决了\(k= 2\)时\(a^2_n\)的数学期望。她想问你对于更大的\(k\),\(E(a^k_n)\)在模\(998244353\)下的值
这里的\(E(a^k_n)\)指\((a_n)^k\)的期望
Sol:
对于\(k=2\)
设\(f(n) = E((a_n)^2)\)
发现还需要一个\(g(n)=E(\sum_{i=0}^{n}a_i^2),t(n)=E(\sum_{i=0}^{n}\sum_{j=0}^{n}a_ia_j)\)
即
而
\(t(n)\)就比较麻烦了
NOIP2016换教室<期望DP + 神奇状态?多个状态加一起>
有\(n\)个课程, 可以申请\(m\)次换教室, 第\(i\)个课程如果申请后成功则去\(d_i\), 失败或不申请都是在\(c_i\), 概率给定
一次性申请, 确定教室后依次走最短路到下一个教室(给图)
问怎样申请期望走的路最短, 输出这个值
Sol:
设\(f[i][j][0/1]\)表示到第\(i\)个教室时申请了\(j\)次, 这次是否申请, 的最小期望
注意不是是否换教室, 而是是否申请, 因为题目要求一次性申请, 前者可能会有一步一步决策的问题; 而后者是一次选择选不选, 所以是正确的
其实选了的DP值是包含两个状态的, 转移时一起考虑其实跟分开考虑是一样的(画分支想想为什么)
总结
- 子问题
- 前缀和优化
- 递推式-->通向式
- 式子化简
- 设置DP的状态, 选择概率/期望
- 遇到高维的期望应当从高维开始推求出需要哪些低维的期望, 而不是把低维推向高维
- 严谨写转移方程