*【学习笔记】(20) 期望与概率
概率
1.基本概念
2.古典概率
3.条件概率
3.1 乘法公式
3.2 全概率公式
3.3 贝叶斯公式
期望
1.期望的线性性质
2.例题
Ⅰ. P4206 [NOI2005] 聪聪与可可
用记忆化搜索来实现:
, ,
点击查看代码
#include<bits/stdc++.h> #define db double using namespace std; const int N = 1e3 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } int n, m, s, t; int tot, Head[N], Next[N << 1], to[N << 1], edge[N << 1]; bool vis[N]; db f[N][N]; int p[N][N], dis[N][N], d[N]; void add(int u, int v, int w){ to[++tot] = v, Next[tot] = Head[u], Head[u] = tot, edge[tot] = w, ++d[u]; } void spfa(int s){ memset(dis, -1, sizeof(dis)); vis[s] = 1, dis[s][s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int x = q.front(); q.pop(); vis[x] = 0; for(int i = Head[x]; i; i = Next[i]){ int y = to[i], z = edge[i]; if(dis[s][y] == -1 || (dis[s][y] == dis[s][x] + z && p[s][x] < p[s][y])){ dis[s][y] = dis[s][x] + z; if(p[s][x]) p[s][y] = p[s][x]; else p[s][y] = y; if(!vis[y]) q.push(y), vis[y] = 1; } } } } db dfs(int x, int y){ if(f[x][y]) return f[x][y]; if(x == y) return f[x][y] = 0.0; if(p[x][y] == y || p[p[x][y]][y] == y) return f[x][y] = 1.0; for(int i = Head[y]; i; i = Next[i]) f[x][y] += dfs(p[p[x][y]][y], to[i]); f[x][y] = (f[x][y] + dfs(p[p[x][y]][y], y)) / (d[y] + 1) + 1; return f[x][y]; } int main(){ n = read(), m = read(), s = read(), t = read(); for(int i = 1; i <= m; ++i){ int u = read(), v = read(); add(u, v, 1), add(v, u, 1); } for(int i = 1; i <= n; ++i) spfa(i); printf("%.3lf\n", dfs(s, t)); return 0; }
Ⅱ. P4745 [CERC2017]Gambling Guide
假设当前处理到
发现如果
有什么影响呢?即
从
为什么我们可以保证这个性质?因为我们试着考虑最短路(
假设所有
那么走到点
得到式子:
化简得:
点击查看代码
#include<bits/stdc++.h> #define db double #define mp make_pair using namespace std; const int N = 3e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } int n, m; int tot, Head[N], Next[N << 1], to[N << 1], d[N], cnt[N]; db f[N], sum[N]; bool vis[N]; priority_queue<pair<double, int> > pq; void add(int u, int v){ to[++tot] = v, Next[tot] = Head[u], Head[u] = tot, ++d[u]; } void dijkstra(int s){ pq.push(mp(0, s)); while(!pq.empty()){ int x = pq.top().second; pq.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = Head[x]; i; i = Next[i]){ int y = to[i]; if(vis[y]) continue; ++cnt[y], sum[y] += f[x]; f[y] = (d[y] + sum[y]) / cnt[y]; pq.push(mp(-f[y], y)); } } } int main(){ n = read(), m = read(); for(int i = 1; i <= m; ++i){ int u = read(), v = read(); add(u, v), add(v, u); } dijkstra(n); printf("%.10lf\n", f[1]); return 0; }
Ⅲ. P2221 [HAOI2012]高速公路
Ⅳ. P4284 [SHOI2014] 概率充电器
树形 dp + 期望。
显然节点
1.它自己来电了 2.它的子树里有一个点来电了传了过来 3.它的子树外面有一个点来电了传了过来
两次 dfs 即可。
点击查看代码
#include<bits/stdc++.h> #define db double #define eps 1e-7 using namespace std; const int N = 5e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } int n; int tot, Head[N], to[N << 1], Next[N << 1], edge[N << 1]; int a[N]; db f[N], ans; void add(int u, int v, int w){ to[++tot] = v, Next[tot] = Head[u], Head[u] = tot, edge[tot] = w; } void dfs1(int x, int fa){ for(int i = Head[x]; i; i = Next[i]){ int y = to[i]; if(y == fa) continue; dfs1(y, x); db pa = f[y] * (0.01 * edge[i]); f[x] = f[x] + pa - f[x] * pa; } } void dfs2(int x, int fa){ ans += f[x]; for(int i = Head[x]; i; i = Next[i]){ int y = to[i]; if(y == fa) continue; db pb = f[y] * (0.01 * edge[i]); if(pb + 1e-7 > 1.0 && pb - 1e-7 < 1.0){ dfs2(y, x); continue; } db pa = (f[x] - pb) / (1 - pb) * (0.01 * edge[i]); f[y] = f[y] + pa - f[y] * pa; dfs2(y, x); } } signed main(){ n = read(); for(int i = 1; i < n; ++i){ int u = read(), v = read(), w = read(); add(u, v, w), add(v, u, w); } for(int i = 1; i <= n; ++i) a[i] = read(), f[i] = a[i] * 0.01; dfs1(1, 0), dfs2(1, 0); printf("%.6lf\n", ans); return 0; }
Ⅴ. P2473 [SCOI2008] 奖励关
dp 式子不难想到,就是关于为什么要逆推,以下是我自己的理解:
1.首先顺推过来的话,因为求的是期望,我们要知道情况数,但由于有些情况是不存在的,所以情况数无法知道,所以只能逆推。
2.有后效性
点击查看代码
#include<bits/stdc++.h> #define db double using namespace std; const int N = 1e2 + 67, M = (1 << 15) + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } int K, n; int p[N], sta[N]; db f[N][M]; signed main(){ K = read(), n = read(); for(int i = 1, x; i <= n; ++i){ p[i] = read(); while(x = read()) sta[i] |= (1 << x - 1); } for(int i = K; i; --i){ for(int j = 0; j < (1 << n); ++j){ for(int k = 1; k <= n; ++k) if((j & sta[k]) == sta[k]) f[i][j] += max(f[i + 1][j], f[i + 1][j | (1 << k - 1)] + p[k]); else f[i][j] += f[i + 1][j]; f[i][j] /= n; } } printf("%.6lf\n", f[1][0]); return 0; }
Ⅵ. P3750 [六省联考 2017] 分手是祝愿
先求出至少需要按多少个灯。
显然每个灯最多按一次,且每一个灯按去的效果都是独一无二的,所以直接从大到小按就行了。
设
点击查看代码
#include<bits/stdc++.h> #define ll long long using namespace std; const int mod = 1e5 + 3, N = 1e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } int n, k, cnt; int a[N]; ll f[N]; ll qsm(ll a, int b){ ll res = 1; for(; b; b >>= 1, a = a * a % mod) if(b & 1) res = res * a % mod; return res; } signed main(){ n = read(), k = read(); for(int i = 1; i <= n; ++i) a[i] = read(); for(int i = n; i; --i){ if(!a[i]) continue; ++cnt; for(int j = 1; j * j <= i; ++j) if(i % j == 0){ a[j] ^= 1; if(j * j != i) a[i / j] ^= 1; } } for(int i = n; i; --i){ ll tmp = ((ll)(n - i) * f[i + 1] + n) % mod; tmp = tmp * qsm(i, mod - 2) % mod; f[i] = tmp; } ll ans = 0; if(cnt <= k) ans = cnt; else{ for(int i = cnt; i > k; --i) ans = (ans + f[i]) % mod; ans = (ans + k) % mod; } for(int i = 1; i <= n; ++i) ans = ans * i % mod; printf("%lld\n", ans); return 0; }
Ⅶ. P3830 [SHOI2012]随机树
- 考虑第一问
设
则
故有
即
-
考虑第二问
-
先证明一个小结论:设扩展
次后,左子树有 个叶子的概率、右子树有 个叶子的概率为 。则有 。
将扩展过程表示为序列,则其中 “扩展左子树” 有
再考虑
同理,右子树的形态数有
故考虑扩展先后、树的形态,生成一个左子树有
与
- 前置芝士:整数概率公式
正整数随机变量
证明:
- 计算概率
设
解释如下,首先考虑左、右子树深度不小于
前文我们证明过,左子树叶节点数为
初始条件,
由整数概率公式可知,期望深度
摘自 https://www.luogu.com.cn/blog/emptyset/solution-p3830
点击查看代码
#include<bits/stdc++.h> #define db double using namespace std; const int N = 1e2 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } int opt, n; db ans, f[N][N]; void solve1(){ for(int i = 2; i <= n; ++i) ans += 2.0 / i; } void solve2(){ for(int i = 1; i <= n; ++i) f[i][0] = 1; for(int i = 2; i <= n; ++i) for(int j = 1; j < i; ++j) for(int k = 1; k < i; ++k) f[i][j] += (f[k][j - 1] + f[i - k][j - 1] - f[k][j - 1] * f[i - k][j - 1]) / (i - 1); for(int i = 1; i < n; ++i) ans += f[n][i]; } signed main(){ opt = read(), n = read(); if(opt & 1) solve1(); else solve2(); printf("%.6lf\n", ans); return 0; }
本文作者:南风未起
本文链接:https://www.cnblogs.com/jiangchen4122/p/17463472.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步