概率dp

概率dp

引入

概率dp用于解决概率问题与期望问题,一般情况下,解决概率问题需要顺序循环,而解决期望问题使用逆序循环,如果定义的状态转移方程存在后效性问题,还需要用到高斯消元来优化。概率dp也会结合其他知识进行考察,例如状态压缩,树上进行 DP 转移等。

dp求概率

这类题目采用顺推,也就是从初始状态推向结果。同一般的dp类似的,难点依然是对状态转移方程的刻画,只是这类题目经过了概率论知识的包装。

例题 Bag of mice

题目大意:袋子里有 \(w\) 只白鼠和 \(b\) 只黑鼠,公主和龙轮流从袋子里抓老鼠。谁先抓到白色老鼠谁就赢,如果袋子里没有老鼠了并且没有谁抓到白色老鼠,那么算龙赢。公主每次抓一只老鼠,龙每次抓完一只老鼠之后会有一只老鼠跑出来。每次抓的老鼠和跑出来的老鼠都是随机的。公主先抓。问公主赢的概率。

过程

\(f_{i,j}\) 为轮到公主时袋子里有 \(i\) 只白鼠,\(j\) 只黑鼠,公主赢的概率。初始化边界,\(f_{0,j}=0\) 因为没有白鼠了算龙赢,\(f_{i,0}=1\) 因为抓一只就是白鼠,公主赢。 考虑 \(f_{i,j}\) 的转移:

  • 公主抓到一只白鼠,公主赢了。概率为 \(\frac{i}{i+j}\)
  • 公主抓到一只黑鼠,龙抓到一只白鼠,龙赢了。概率为 \(\frac{j}{i+j}\cdot \frac{i}{i+j-1}\)
  • 公主抓到一只黑鼠,龙抓到一只黑鼠,跑出来一只黑鼠,转移到 \(f_{i,j-3}\)。概率为 \(\frac{j}{i+j}\cdot\frac{j-1}{i+j-1}\cdot\frac{j-2}{i+j-2}\)
  • 公主抓到一只黑鼠,龙抓到一只黑鼠,跑出来一只白鼠,转移到 \(f_{i-1,j-2}\)。概率为 \(\frac{j}{i+j}\cdot\frac{j-1}{i+j-1}\cdot\frac{i}{i+j-2}\)
    考虑公主赢的概率,第二种情况不参与计算。并且要保证后两种情况合法,所以还要判断 \(i,j\) 的大小,满足第三种情况至少要有 \(3\) 只黑鼠,满足第四种情况要有 \(1\) 只白鼠和 \(2\) 只黑鼠。

实现

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1010;

int w, b;
double dp[kMaxN][kMaxN];

int main() {
  cin >> w >> b;
  memset(dp, 0, sizeof(dp));
  for (int i = 1; i <= w; i++) dp[i][0] = 1;  // 初始化
  for (int i = 1; i <= b; i++) dp[0][i] = 0;
  for (int i = 1; i <= w; i++) {
    for (int j = 1; j <= b; j++) {  // 以下为题面概率转移
      dp[i][j] += (double)i / (i + j);
      if (j >= 3) {
        dp[i][j] += (double)j / (i + j) * (j - 1) / (i + j - 1) * (j - 2) / (i + j - 2) * dp[i][j - 3];
      }
      if (i >= 1 && j >= 2) {
        dp[i][j] += (double)j / (i + j) * (j - 1) / (i + j - 1) * i / (i + j - 2) * dp[i - 1][j - 2];
      }
    }
  }
  printf("%.9lf\n", dp[w][b]);
  return 0;
}

dp求期望

例题1 Collecting Bugs

题目大意

一个软件有 \(s\) 个子系统,会产生 \(n\)\(bug\)。某人一天发现一个 \(bug\),这个 \(bug\) 属于某种 \(bug\) 分类,也属于某个子系统。每个 \(bug\) 属于某个子系统的概率是 \(\frac{1}{s}\),属于某种 \(bug\) 分类的概率是 \(\frac{1}{n}\)。求发现 \(n\)\(bug\),且 \(s\) 个子系统都找到 \(bug\) 的期望天数。

过程

\(f_{i,j}\) 为已经找到 \(i\)\(bug\) 分类,\(j\) 个子系统的 \(bug\),达到目标状态的期望天数。这里的目标状态是找到 \(n\)\(bug\) 分类,\(s\) 个子系统的 \(bug\)。那么就有 \(f_{n,s}=0\),因为已经达到了目标状态,不需要用更多的天数去发现 \(bug\) 了,于是就以目标状态为起点开始递推,答案是 \(f_{0,0}\)

考虑 \(f_{i,j}\) 的状态转移:

  • \(f_{i,j}\),发现一个 \(bug\) 属于已经发现的 \(i\)\(bug\) 分类,\(j\) 个子系统,概率为 \(p_1=\frac{i}{n}\cdot\frac{j}{s}\)
  • \(f_{i,j+1}\),发现一个 \(bug\) 属于已经发现的 \(i\)\(bug\) 分类,不属于已经发现的子系统,概率为 \(p_2=\frac{i}{n}\cdot(1-\frac{j}{s})\)
  • \(f_{i+1,j}\),发现一个 \(bug\) 不属于已经发现 \(bug\) 分类,属于 \(j\) 个子系统,概率为 \(p_3=(1-\frac{i}{n})\cdot\frac{j}{s}\)
  • \(f_{i+1,j+1}\),发现一个 \(bug\) 不属于已经发现 \(bug\) 分类,不属于已经发现的子系统,概率为 \(p_4=(1-\frac{i}{n})\cdot(1-\frac{j}{s})\)
    再根据期望的线性性质,就可以得到状态转移方程:

\(\begin{aligned} f_{i,j} &= p_1\cdot f_{i,j}+p_2\cdot f_{i,j+1}+p_3\cdot f_{i+1,j}+p_4\cdot f_{i+1,j+1} + 1\\ &= \frac{p_2\cdot f_{i,j+1}+p_3\cdot f_{i+1,j}+p_4\cdot f_{i+1,j+1}+1}{1-p_1} \end{aligned}\)

实现

#include <cstdio>

using namespace std;

const int kMaxN = 1010;

int n, s;
double dp[kMaxN][kMaxN];

int main() {
  scanf("%d %d", &n, &s);
  dp[n][s] = 0;
  for (int i = n; i >= 0; i--) {
    for (int j = s; j >= 0; j--) {
      if (i == n && s == j) continue;
      dp[i][j] = (dp[i][j + 1] * i * (s - j) + dp[i + 1][j] * (n - i) * j + dp[i + 1][j + 1] * (n - i) * (s - j) + n * s) / (n * s - i * j);  // 概率转移
    }
  }
  printf("%.4lf\n", dp[0][0]);
  return 0;
}

例题2 飞行棋

题目大意

有一架飞机一开始在 \(n\) 号位置。牛牛每回合可以先投掷一次 \(d\) 面的骰子,\(1\)\(d\) 等概率出现。投出点数 \(x\) 后,飞机会移动 \(x\) 步,每步移动一格,方向初始向左移动,若到达终点,会向右移动。若投出的点数为 \(d\) 点,可以继续投掷,直到投出的点数不是 \(d\) 点。求让这架飞机停在 \(0\) 号位置回合数的期望。

过程

\(dp_x\) 为从 \(x\) 走到 \(1\) 的步数。

  • \(x\geq d\) 时,\(dp_x=\sum_{i=1}^d\frac{dp_x-1}{d}\)
  • \(X<D\)时,期望为 \(d-1\)

实现

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1e5 + 10;

int t, n, d;
double dp[kMaxN], s;

int main() {
  for (cin >> t; t--;) {
    cin >> n >> d;
    dp[0] = 1, s = 0;
    for (int i = 1; i < d; i++) {
      dp[i] = d - 1;
      s += dp[i] + 1;
    }
    for (int i = d; i <= n; i++) {
      dp[i] = dp[i - d] / d + s / d;
      s = s - dp[i - d + 1] + dp[i];
    }
    printf("%.2lf\n", dp[n]);
  }
  return 0;
}
posted @ 2024-07-25 10:45  小熊涛涛  阅读(6)  评论(0编辑  收藏  举报