概率与期望
概念讲解
基本概念
- 概率: 事件发生的可能性,范围 [0, 1]
- 期望: 随机变量的平均值
期望的性质
- E(aX + b) = aE(X) + b
- E(X + Y) = E(X) + E(Y)
- E(XY) = E(X)E(Y)(X, Y 独立)
期望 DP
基本思想
利用期望的线性性,通过 DP 计算期望。
示例:随机游走
12345678double dp[N];
// dp[i] 表示从状态 i 到终点的期望步数
dp[n] = 0; // 终点
for (int i = n - 1; i >= 0; i--) {
dp[i] = 1 + dp[i + 1]; // 走一步到 i+1
}经典问题
1. 骰子问题
问题: 掷 n 个骰子,求点数之和的期望。
解答:
2. 抽签问题
问题: 从 n 个球中随机抽取,求抽到特定球的期望次数。
解答:
3. 随机游走
问题: 在数轴上随机游走,每次等概率向左或向右走一步,求到达终点的期望步数。
123456789double expectedSteps(int n) {
vector<double> dp(n + 1, 0);
for (int i = n - 1; i >= 0; i--) {
dp[i] = 1 + 0.5 * dp[i + 1] + 0.5 * dp[max(0, i - 1)];
}
return dp[0];
}考点分析
初赛考查方式
- 选择题: 概率和期望的概念
- 程序阅读: 理解期望 DP
- 编程题: 概率与期望应用
常见题型
- 期望 DP
- 概率计算
- 随机游走
例题精讲
例题 1:骰子问题
题目: 掷 n 个骰子,求点数之和的期望。
解答:
12345678910111213#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
double expected = n * 3.5;
cout << expected << endl;
return 0;
}例题 2:随机游走
题目: 在数轴上随机游走,求到达终点的期望步数。
解答:
123456789101112131415161718#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<double> dp(n + 1, 0);
for (int i = n - 1; i >= 0; i--) {
dp[i] = 1 + 0.5 * dp[i + 1] + 0.5 * dp[max(0, i - 1)];
}
cout << dp[0] << endl;
return 0;
}练习题
- 洛谷 P1291 随机数生成器
查看答案
思路:期望 DP,计算到达目标值的期望步数。
123456789101112#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
// 期望步数 = n
cout << n << endl;
return 0;
}- 洛谷 P1365 WJMZBMR T 了什么
查看答案
思路:期望 DP,计算期望长度。
12345678910111213141516171819202122232425262728#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int n;
scanf("%d", &n);
char s[n + 1];
scanf("%s", s);
double ans = 0, cnt = 0;
for (int i = 0; i < n; i++) {
if (s[i] == 'o') {
cnt++;
ans += 2 * cnt - 1;
} else if (s[i] == 'x') {
cnt = 0;
} else {
ans += cnt;
cnt = (cnt + 1) / 2;
}
}
printf("%.4f\n", ans);
return 0;
}- 洛谷 P1850 换教室
查看答案
思路:期望 DP,考虑申请成功和失败两种情况。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int n, m, v, e;
scanf("%d %d %d %d", &n, &m, &v, &e);
int c[n + 1], d[n + 1];
double k[n + 1];
for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
for (int i = 1; i <= n; i++) scanf("%lf", &k[i]);
int dist[v + 1][v + 1];
for (int i = 1; i <= v; i++) {
for (int j = 1; j <= v; j++) {
dist[i][j] = (i == j ? 0 : 1e9);
}
}
for (int i = 0; i < e; i++) {
int a, b, w;
scanf("%d %d %d", &a, &b, &w);
dist[a][b] = dist[b][a] = min(dist[a][b], w);
}
// Floyd
for (int k = 1; k <= v; k++) {
for (int i = 1; i <= v; i++) {
for (int j = 1; j <= v; j++) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
// DP
double dp[n + 1][m + 1][2];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
dp[i][j][0] = dp[i][j][1] = 1e9;
}
}
dp[1][0][0] = dp[1][1][1] = 0;
for (int i = 2; i <= n; i++) {
for (int j = 0; j <= min(i, m); j++) {
// 不申请
dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0] + dist[c[i - 1]][c[i]]);
dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][1] + dist[c[i - 1]][c[i]] * (1 - k[i - 1]) + dist[d[i - 1]][c[i]] * k[i - 1]);
if (j > 0) {
// 申请
dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][0] + dist[c[i - 1]][c[i]] * (1 - k[i]) + dist[c[i - 1]][d[i]] * k[i]);
dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][1] + dist[c[i - 1]][c[i]] * (1 - k[i - 1]) * (1 - k[i]) + dist[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) + dist[c[i - 1]][d[i]] * (1 - k[i - 1]) * k[i] + dist[d[i - 1]][d[i]] * k[i - 1] * k[i]);
}
}
}
double ans = 1e9;
for (int j = 0; j <= m; j++) {
ans = min(ans, min(dp[n][j][0], dp[n][j][1]));
}
printf("%.2f\n", ans);
return 0;
}易错点
⚠️ 注意:
- 期望具有线性性
- 期望 DP 要注意状态转移
- 概率和期望是不同的概念
- 期望 DP 通常要倒序计算