期望DP

这篇博客介绍了期望DP的概念和性质,包括定义和常见的性质。通过解析几个例题,如路径长度、乘坐电梯、期望收益和期望分数,阐述了如何应用期望DP解决问题。博主强调在解决期望DP问题时,通常需要转化问题并利用概率和期望的性质进行状态转移。最后,博客还给出了相关习题以供读者练习。

期望 DP

期望

定义

性质

C C C 为常数, X , Y X,Y X,Y 为随机变量

  1. E ( C ) = C E(C)=C E(C)=C
  2. E ( C x ) = C E ( x ) E(Cx)=CE(x) E(Cx)=CE(x)
  3. E ( x + y ) = E ( x ) + E ( y ) E(x+y)=E(x)+E(y) E(x+y)=E(x)+E(y)
  4. x , y x,y x,y 相互独立时 E ( x y ) = E ( x ) E ( y ) E(xy)=E(x)E(y) E(xy)=E(x)E(y)

注:3,4 条是重点

期望 DP

期望 DP,是一种 DP,所以核心思想与一般的 DP 差不多(事实上,并没有什么特别的)。
一般,设 f [ i ] [ j ] f[i][j] f[i][j] 为此状态的期望,状态转移时,将上一个状态的值和花费的和乘以转移的概率求和。(如成环,高斯消元)

例题

路径长度
题目描述

给定一起点为 1 1 1,终点为 n n n 的有向无环图,对于一个出度为 K K K 的点,走每条边离开的概率为 1 K \cfrac{1}{K} K1。求从起点到终点的路径期望长度。


这是一个引入。
利用性质 3,问题转化为求出经过每条边的期望次数,不过这并不好求,所以我们再转化为求每个节点的期望经过次数(这是一种十分常见的操作)。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

struct node {
    int to, next, w;
} edge[200005];

int t[100005], cnt = 1, out[100005], in[100005];
int q[200005];
double ans, f[100005];
int n, m;

void create(int u, int v, int w) {
    edge[cnt].next = t[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    t[u] = cnt++;
    out[u]++;
}

void solve() {
    int l = 0, r = -1;
    q[++r] = 1;
    f[1] = 1;
    while (l <= r) {
        int _ = q[l++];
        for (int i = t[_]; i; i = edge[i].next) {
            int __ = edge[i].to;
            f[__] += f[_] / out[_];
            ans += f[_] * edge[i].w / out[_];
            if (--in[__] == 0)
                q[++r] = __;
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        create(u, v, w);
        in[v]++;
    }
    solve();
    printf("%.2f\n", ans);
    return 0;
}

乘坐电梯
题目描述

n n n 个人排成一列,每秒钟队首的人有 p p p 的概率走上电梯,或 1 − p 1-p 1p 的概率不动。求 T T T 秒后,电梯上人数的期望。


这就是一道十分模版的题了
设: d p ( i , j ) dp(i,j) dp(i,j) 为第 i i i 秒电梯上有 j j j 个人的概率。决策是:上和不上。所以就有:
d p ( i , j ) ⋅ p → d p ( i + 1 , j + 1 ) d p ( i , j ) ⋅ ( 1 − p ) → d p ( i + 1 , j ) dp(i,j)\cdot p\to dp(i+1,j+1)\\ dp(i,j)\cdot (1-p)\to dp(i+1,j) dp(i,j)pdp(i+1,j+1)dp(i,j)(1p)dp(i+1,j)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

double p, dp[2005][2005];
int n, t;

int main() {
    scanf("%d%lf%d", &n, &p, &t);

    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    double p0 = 1 - p;

    for (int i = 0; i < t; i++) {
        dp[i + 1][n] += dp[i][n];
        for (int j = 0; j < n; j++)
            if (dp[i][j] > 0)
                dp[i + 1][j + 1] += dp[i][j] * p, dp[i + 1][j] += dp[i][j] * p0;
    }
    double ans = 0;
    for (int i = 0; i <= n; i++) {
        ans += dp[t][i] * i;
    }
    printf("%.6f\n", ans);
    return 0;
}

期望收益
题目描述

给定一个序列,一些位置未被确定,是 o o o x x x 的概率各 50 % 50\% 50%,对于一个 o x ox ox公牛)序列,连续的 a a a 长度的 o o o 可得到 a 2 a^2 a2 的收益,求期望收益。


第 3 条性质只适用于一次,所以我们要尝试把二次变成一次。
( x + 1 ) 2 − x 2 = 2 x + 1 (x+1)^2-x^2=2x+1 (x+1)2x2=2x+1,那么只需要两个量:至某一位置之前的 a a a 的期望,和期望收益。每次用期望长度乘 2 加 1,再乘上概率,就是增加的收益。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;

double dp[300005], l[300005];
string s;
int n;

int main() {
    cin >> n;
    cin >> s;
    int len = s.size();
    for (int i = 1; i <= len; i++) {
        if (s[i - 1] == 'x') {
            dp[i] = dp[i - 1];
            l[i] = 0;
            continue;
        }
        if (s[i - 1] == 'o') {
            dp[i] = 2 * l[i - 1] + 1 + dp[i - 1];
            l[i] = l[i - 1] + 1;
            continue;
        }
        dp[i] = dp[i - 1] + l[i - 1] + 0.5;
        l[i] = (l[i - 1] + 1) / 2;
    }
    printf("%.4f", dp[len]);
    return 0;
}

期望分数
题目描述

还是 o x ox ox 序列,每个位置为 o o o 的概率为 p p p x x x ( 1 − p ) (1-p) (1p),连续的 a a a 长度的 o o o 可得到 a 3 a^3 a3 的收益,求期望收益。


和上一题挺像,但又高了一次,还多了个概率。
k 1 = ( x + 1 ) 3 − x 3 = 3 x 2 + 3 x + 1 k_1=(x+1)^3-x^3=3x^2+3x+1 k1=(x+1)3x3=3x2+3x+1
k 2 = ( x + 1 ) 2 − x 2 = 2 x + 1 k_2=(x+1)^2-x^2=2x+1 k2=(x+1)2x2=2x+1
先求 k 2 k_2 k2,再把 k 2 k_2 k2 带入 k 1 k_1 k1

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

double dp, l, d;
int n;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        double p;
        cin >> p;
        dp += (d + 3 * l + 1) * p;
        d = (d + l * 6 + 3) * p;
        l = (l + 1) * p;
    }
    printf("%.1f", dp);
    return 0;
}

习题

彩色圆环

我们地方的荷花又在这陌生的水上开了花,放出同样的清香,只是名字换了。——泰戈尔

其实DP就像荷花,无论如何变化,其核心是相通的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值