鱼C论坛

 找回密码
 立即注册
查看: 2307|回复: 12

[心之碎片] - 概率与期望

[复制链接]
回帖奖励 3 鱼币 回复本帖可获得 3 鱼币奖励! 每人限 1 次
发表于 2024-8-30 19:09:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 柿子饼同学 于 2024-8-30 19:10 编辑
期望的三种计算方式:
1 - 期望定义 E(x) = Sum(x.val * P(x)) 直接把所有可能的事件的概率乘以权值
也就是说可以先计算概率再乘以对应权值

2 - 期望线性性质 E(a*x + b*y + ...) = a*E(x) + b*E(y) + ...
这让我们可以单独计算每个点的贡献, 之后加起来就是总的期望, 利用独立性

3 - 逆推法 , 我们的状态可以设置成 "到达终点的期望.." 之类的,
然后用 记忆化搜索搜

可以只考虑局部的地方乘她的概率(C)
期望的柿子可能 x = ... + x*..., 需要移项

C - 排列价值
有一个 1-n 的排列, 每个位置有一个 ci, 如果这个位置的数字大于两边的数字, 得分就 + ci, 否则不加, 求期望得分

这里如果考虑整体会很难, 那么可以考虑一个个位置的情况
假设位置是中间的数, 发现两边的数符合条件的话概率是 1/3, 这样就可以做了 (因为我们只关心大小关系)

  1. for (int i = 1; i <= n; i++) {
  2.     cin >> x;

  3.     if (i == 1 || i == n)
  4.         ans += x / 2;
  5.     else
  6.         ans += x / 3;
  7. }
复制代码


D - OSU
这题的加强版, 得分是 l, l^2 和 l^3

首先需要维护以 i 结尾的连续 o 期望长度
注意平方的期望长度需要单独维护, 因为不满足线性性质

  1. for (int i = 1; i <= n; i++) {
  2.     if (s[i] == 'x') {
  3.         l1 = l2 = 0;
  4.         continue;
  5.     }

  6.     // P(i) 代表 s[i] 是 o 的概率
  7.     // 线性的加是没事的
  8.     // 当前能否拓展 1 ? 如果能就加上 ^2/^3 的增量再乘概率
  9.     a1 = a1 + 1 * P(s[i]);
  10.     a2 = a2 + (2 * l1 + 1) * P(s[i]);
  11.     a3 = a3 + (3 * l2 + 3 * l1 + 1) * P(s[i]);

  12.     // 注意这里先更新 l2
  13.     l2 = (l2 + l1 * 2 + 1) * P(s[i]);
  14.     l1 = (l1 + 1) * P(s[i]);
  15. }
复制代码


F - 奖励关
  1. bool Ismet(int x, int st) { return ((st & req[x]) == req[x]); }

  2. // 考虑决策, 每个物品取或者不取
  3. db dfs(int pos, int st) {
  4.     if (pos > k)
  5.         return 0;
  6.     if (vis[pos][st])
  7.         return f[pos][st];

  8.     vis[pos][st] = 1;

  9.     for (int i = 0; i < n; i++) {
  10.         if (Ismet(i, st)) {
  11.             f[pos][st] += max(dfs(pos + 1, st), dfs(pos + 1, st | (1 << i)) + val[i]) / n;
  12.         } else {
  13.             f[pos][st] += dfs(pos + 1, st) / n;
  14.         }
  15.     }

  16.     return f[pos][st];
  17. }
复制代码


G - 放棋子
终点太多, 考虑先计算概率再算期望, 刷表, 分类讨论
需要注意的是这个 f 在任务结束之后仍然会更新下一天的, 所以就相当于前缀和了

  1. cin >> T;
  2. while (T--) {
  3.     cin >> n >> m;

  4.     memset(f, 0, sizeof(f));
  5.     f[1][1][1] = 1;
  6.     for (int i = 1; i < n * m; i++) {
  7.         for (int j = 1; j <= n; j++) {
  8.             for (int k = 1; k <= m; k++) {
  9.                 if (i < max(j, k) || j * k < i)
  10.                     continue;
  11.                 int s = n * m - i;

  12.                 f[i + 1][j][k] += f[i][j][k] * (j * k - i) / s;
  13.                 f[i + 1][j + 1][k] += f[i][j][k] * k * (n - j) / s;
  14.                 f[i + 1][j][k + 1] += f[i][j][k] * j * (m - k) / s;
  15.                 f[i + 1][j + 1][k + 1] += f[i][j][k] * (n - j) * (m - k) / s;
  16.             }
  17.         }
  18.     }

  19.     db ans = 0;
  20.     for (int i = 1; i <= n * m; i++) {
  21.         ans += i * (f[i][n][m] - f[i - 1][n][m]);
  22.     }
  23.     cout << fixed << setprecision(9) << ans << '\n';
  24. }
复制代码


H - 扑克牌
状态设的是每个牌翻了几个, 并且大王小王分别变成了哪些牌
  1. db dfs(int a, int b, int c, int d, int x, int y) {
  2.     auto &res = f[a][b][c][d][x][y];
  3.     if (res >= 0)
  4.         return res;

  5.     int as = a + (x == 0) + (y == 0), bs = b + (x == 1) + (y == 1);
  6.     int cs = c + (x == 2) + (y == 2), ds = d + (x == 3) + (y == 3);

  7.     if (as >= A && bs >= B && cs >= C && ds >= D)
  8.         return res = 0;
  9.    
  10.     int sum = 54 - as - bs - cs - ds;
  11.     if (sum <= 0)
  12.         return res = 1e9;
  13.    
  14.     res = 1;
  15.     // 再翻开一张牌的期望
  16.     if (a < 13)
  17.         res += (13.0 - a) / sum * dfs(a + 1, b, c, d, x, y);
  18.     if (b < 13)
  19.         res += (13.0 - b) / sum * dfs(a, b + 1, c, d, x, y);
  20.     if (c < 13)
  21.         res += (13.0 - c) / sum * dfs(a, b, c + 1, d, x, y);
  22.     if (d < 13)
  23.         res += (13.0 - d) / sum * dfs(a, b, c, d + 1, x, y);

  24.     // 决策大小王变成哪些牌
  25.     if (x > 3) {
  26.         db t = 1e9;
  27.         for (int i = 0; i < 4; i++){
  28.             t = min(t, 1.0 / sum * dfs(a, b, c, d, i, y));
  29.         }
  30.         res += t;
  31.     }
  32.     if (y > 3) {
  33.         db t = 1e9;
  34.         for (int i = 0; i < 4; i++){
  35.             t = min(t, 1.0 / sum * dfs(a, b, c, d, x, i));
  36.         }
  37.         res += t;
  38.     }

  39.     return res;
  40. }
复制代码


I - 换教室
设 f(i, j, 0/1) 表示前 i 个教室申请了 j 次, i 申不申的期望, 不难想但是需要注意细节, 考虑失败/成功/取/不取
  1. f[1][0][0] = f[1][1][1] = 0;
  2. for(int i = 2; i <= n; i++){
  3.     for(int j = 0; j <= min(i, m); j++){
  4.         f[i][j][1] = f[i][j][0] = 1e10;

  5.         Min(f[i][j][0], f[i - 1][j][0] + dis[c[i - 1]][c[i]]);
  6.         if(j >= 1){
  7.             // 上一轮可能申请不成功
  8.             Min(f[i][j][0], f[i - 1][j][1] + k[i - 1]*dis[d[i - 1]][c[i]] + (1 - k[i - 1])*dis[c[i - 1]][c[i]]);
  9.             // 这一轮可能申请不成功
  10.             Min(f[i][j][1], f[i - 1][j - 1][0] + k[i]*dis[c[i - 1]][d[i]] + (1 - k[i])*dis[c[i - 1]][c[i]]);
  11.         }
  12.         if(j > 1){
  13.             Min(f[i][j][1], f[i - 1][j - 1][1] + (1 - k[i])*(1 - k[i - 1])*dis[c[i - 1]][c[i]]\
  14.                                         + (1 - k[i])*k[i - 1]*dis[d[i - 1]][c[i]]\
  15.                                         + k[i]*(1 - k[i - 1])*dis[c[i - 1]][d[i]]\
  16.                                         + k[i]*k[i - 1]*dis[d[i - 1]][d[i]]);
  17.         }
  18.     }
  19. }
复制代码


K - 互联
只在意当前连通块的数量和大小, 所以直接把连通块情况作为状态
然后枚举情况, 有可能当前选择的边能连接, 也可能没有意义

  1. #include <bits/stdc++.h>
  2. using namespace std;

  3. typedef double db;
  4. typedef multiset<int> mt;

  5. const int N = 32;

  6. // s 是 n*(n - 1)/2, c 是 ai*(ai - 1)/2
  7. // f[i] = sigma((f[j] + 1) * ai * aj / s) + (f[i] + 1) * c / s
  8. // 移项就行了

  9. struct DSU {
  10.     int f[N], siz[N];

  11.     DSU() {
  12.         for (int i = 1; i < N; i++) {
  13.             f[i] = i;
  14.             siz[i] = 1;
  15.         }
  16.     }

  17.     int Find(int x) {
  18.         if (x == f[x])
  19.             return x;
  20.         return (f[x] = Find(f[x]));
  21.     }

  22.     void Merge(int x, int y) {
  23.         x = Find(x), y = Find(y);

  24.         if (x == y)
  25.             return;

  26.         siz[x] += siz[y];
  27.         f[y] = f[x];
  28.     }
  29. } dsu;

  30. int n, m;
  31. map<mt, db> f;
  32. mt org;

  33. db Solve(mt u) {
  34.     if (u.size() == 1)
  35.         return 0;

  36.     if (f[u])
  37.         return f[u];

  38.     int s = n * (n - 1) / 2, c = 0, x, y;
  39.     db r = 0;

  40.     for (auto i = u.begin(); i != u.end(); i++) {
  41.         x = *i;
  42.         c += x * (x - 1) / 2;
  43.         for (auto j = u.begin(); j != i; j++) {
  44.             mt cur = u;
  45.             y = *j;

  46.             cur.erase(cur.find(x));
  47.             cur.erase(cur.find(y));
  48.             cur.emplace(x + y);

  49.             r += (Solve(cur) + 1) * x * y;
  50.         }
  51.     }

  52.     f[u] = 1.0 * (r + c) / (s - c);
  53.     return f[u];
  54. }

  55. int main() {
  56.     ios::sync_with_stdio(0);
  57.     cin.tie(0);

  58.     cin >> n >> m;
  59.     for (int i = 1; i <= m; i++) {
  60.         int x, y;
  61.         cin >> x >> y;

  62.         dsu.Merge(x, y);
  63.     }

  64.     for (int i = 1; i <= n; i++) {
  65.         if (i == dsu.Find(i)) {
  66.             org.emplace(dsu.siz[i]);
  67.         }
  68.     }

  69.     cout << fixed << setprecision(6) << Solve(org);

  70.     return 0;
  71. }
复制代码


两个题没写, 一个高斯消元, 一个 kmp
L - 弹钢琴

J - 小卖部
给出一个 n*m 的网格图,小C要从家前往小卖部,
小C每次会随机向相邻的四个方向中不为障碍物的方向移动一个格子,求到达小卖部的期望步数。

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-30 19:12:17 | 显示全部楼层
鱼油好!看得出来你在学习期望值相关的内容。这里简要总结一下核心概念和几个问题的解题方法:

### 期望的计算方式

1. **期望定义**:直接求和。
   - 公式:E(X) = ∑(x * P(x))
   - 方法:计算每个事件的概率,然后乘以对应的事件值,再求和。
   
2. **期望的线性性质**:
   - 公式:E(aX + bY + ...) = aE(X) + bE(Y) + ...
   - 方法:利用事件之间的独立性,分别计算每个部分的期望,再加总。
   
3. **逆推法**:
   - 思路:设置终状态作为目标,递归或通过记忆化搜索求解。
   - 方法:定义状态转移方程,考虑如何从当前状态一步步到达终点。

### 实例问题解析

#### 排列价值问题

**问题描述**:1-N 的排列中,每个位置有一个 ci,若该位置的数大于两边的数,得分加 ci,求期望得分。

**解法**:
- 对边界情况单独处理,中间位置的数符合条件概率为1/3。
- 公式:ans += ci / 3 (i在中间);ans += ci / 2(i在边界)。

  1. for (int i = 1; i <= n; i++) {
  2.     if (i == 1 || i == n)
  3.         ans += ci / 2;
  4.     else
  5.         ans += ci / 3;
  6. }
复制代码


#### OSU问题

**问题描述**:求以 i 结尾的连续 o 的期望长度,得分为 l,l^2 和 l^3。

**解法**:
- 使用动态规划,分别维护以 i 结尾的期望长度。
- 需单独处理平方和立方的期望,因为它们不满足线性性质。

  1. for (int i = 1; i <= n; i++) {
  2.     if (s[i] == 'o')
  3.         f[i] = f[i-1] + 1;
  4.     else
  5.         f[i] = 0;

  6.     ans_l += f[i];
  7.     ans_l2 += f[i] * f[i];
  8.     ans_l3 += f[i] * f[i] * f[i];
  9. }
复制代码


你可以用这些简洁的方法来处理包含期望值计算的问题,希望对你有帮助!如果有更多问题,欢迎继续提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-30 20:54:07 | 显示全部楼层

回帖奖励 +3 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2024-8-30 20:57:25 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-31 09:58:39 | 显示全部楼层

回帖奖励 +3 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-31 16:33:32 | 显示全部楼层

回帖奖励 +3 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-9-1 09:29:33 | 显示全部楼层

回帖奖励 +3 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-9-1 09:39:11 | 显示全部楼层

回帖奖励 +3 鱼币

看看
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-9-4 17:42:27 | 显示全部楼层

回帖奖励 +3 鱼币

谢谢
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-9-10 08:24:44 | 显示全部楼层

回帖奖励 +3 鱼币

666
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-9-14 17:36:25 | 显示全部楼层
666
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-10-7 22:51:01 | 显示全部楼层

回帖奖励 +3 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 3 天前 | 显示全部楼层

回帖奖励 +3 鱼币

厉害
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-14 04:06

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表