「算法笔记」期望 DP 入门

一、数学期望

1. 由来

\(17\) 世纪,有一个赌徒向法国著名数学家帕斯卡挑战,给他出了一道题目:甲乙两个人赌博,他们两人获胜的机率相等,比赛规则是先胜三局者为赢家,一共进行五局,赢家可以获得 \(100\) 法郎的奖励。当比赛进行到第四局的时候,甲胜了两局,乙胜了一局,这时由于某些原因中止了比赛,那么如何分配这 \(100\) 法郎才比较公平?

甲输掉后两局的可能性只有 \(\frac{1}{2} \times \frac{1}{2}=\frac{1}{4}\),也就是说甲赢得后两局或后两局中任意赢一局的概率为 \(1-\frac{1}{4}=\frac{3}{4}\),甲有 \(75\%\) 的期望获得 \(100\) 法郎;而乙期望赢得 \(100\) 法郎就得在后两局均击败甲,乙连续赢得后两局的概率为 \(\frac{1}{2}\times \frac{1}{2}=\frac{1}{4}\),即乙有 \(25\%\) 的期望获得 \(100\) 法郎奖金。

可见,虽然不能再进行比赛,但依据上述可能性推断,甲乙双方最终胜利的客观期望分别为 \(75\%\)\(25\%\),因此甲应分得奖金的 \(100\times 75\%=75\) (法郎),乙应分得奖金的的 \(100×25\%=25\) (法郎)。这个故事里出现了“期望”这个词,数学期望由此而来。(¿)

(摘自百度百科)

2. 定义

数学期望是实验中每次可能结果的概率乘以其结果的总和,它的定义式为:

\[E(X)=\sum\limits_{k=1}^\infty x_i p_i \]

其中 \(i\) 是所有可能发生的事件,\(x_i\) 为事件的权值,\(p_i\) 为事件发生的概率。

3. 性质

\(C\) 为一个常数, \(X\)\(Y\) 是两个随机变量。以下是数学期望的重要性质:

  • 1. \(E(C)=C\)
  • 2. \(E(CX)=CE(X)\)
  • 3. 期望的 线性性\(E(X+Y)=E(X)+E(Y)\)
  • 4. 当 \(X\)\(Y\) 相互独立时,\(E(XY)=E(X)E(Y)\)

二、例题

1. SP1026 FAVDICE - Favorite Dice

题目大意:一个 \(n\) 面的骰子,求期望掷几次能使得每一面都被掷到。\(n\leq 6000\)

Solution:

结论:\(1+\frac{n}{n-1}+\frac{n}{n-2}+...+n=\sum_{i=1}^n \frac{n}{i}\)

\(f_i\) 表示已经掷出 \(i\) 个不同的面,还期望掷多少次能使得每一面都被掷到。

显然 \(f_n=0\)

对于所有 \(f_i\ (i\neq n)\),有两种情况:

  • \(\frac{i}{n}\) 的概率掷到重复的面,则还需掷 \(f_i\) 次。
  • \(\frac{n-i}{n}\) 的概率掷到新的面,则还需掷 \(f_{i+1}\) 次。

\(f_i=(\frac{i}{n} f_i+\frac{n-i}{n} f_{i+1})+1\)

整理得,\(f_i=f_{i+1}+\frac{n}{n-i}\)

因此 \(Ans=f_0=\sum\limits_{i=n-1}^{0} \frac{n}{n-i}=\sum_{i=1}^n \frac{n}{i}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n;
double ans;
signed main(){
    scanf("%lld",&t);
    while(t--){
        scanf("%lld",&n),ans=0;
        for(int i=1;i<=n;i++)
            ans+=1.0*n/i;
        printf("%.2lf\n",ans);
    }
    return 0;
}

2. BZOJ 1419 Red is good

题目大意:桌面上有 \(R\) 张红牌和 \(B\) 张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到 \(1\) 美元,黑牌则付出 \(1\) 美元。可以随时停止翻牌,在最优策略下平均能得到多少钱。\(0\leq R,B \leq 5000\)

Solution:

\(f_{r,b}\) 表示剩下 \(r\) 张红牌、\(b\) 张黑牌的期望收益。

首先考虑边界情况。

\(r=0\),即目前已经没有红牌时,停止翻牌,则 \(f_{r,b}=0\)。当 \(b=0\),即剩下的牌没有黑牌时,肯定会把剩下的红牌全部翻掉,则 \(f_{r,b}=r\)

讨论其他情况。

\(\frac{r}{r+b}\) 的概率翻到一张红牌,并带来 \(1\) 的收益;有 \(\frac{b}{r+b}\) 的概率翻到一张黑牌,并带来 \(1\) 的损失。

那么显然 \(f_{r,b}=\max(0,\frac{r}{r+b}(1+f_{r-1,b})+\frac{b}{r+b}(-1+f_{r,b-1})\)。因为我们采用的是最优策略,所以当此时的局面已经不足以带来大于 \(0\) 的期望收益时,应该停止翻牌,所以最后还要和 \(0\) 取 \(max\)。

由于卡空间,需要使用滚动数组。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=5010;
int n,m;
double f[2][N],ans;
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int r=1;r<=n;r++){ 
        f[r%2][0]=1.0*r;
        for(int b=1;b<=m;b++)
            f[r%2][b]=max(0.0,1.0*r/(r+b)*(1+f[(r-1)%2][b])+1.0*b/(r+b)*(-1+f[r%2][b-1]));
    } 
    printf("%.6lf\n",1.0*floor(f[n%2][m]*1e6)/1e6);
    return 0;
}

3. BZOJ 4318 OSU!

题目大意:给出一串数,每个数字有 \(a_i\) 的概率是 \(\text{O}\),这串数字的分数定义为每一段极长连续的 \(\text{O}\) 的长度的立方和,\(\text{OXXOO}\) 的分数就是 \(1^3+2^3=9\),求期望分数。\(N\leq 10^5\)

Solution:

\(y_i\) 表示到 \(i\) 为止连续打出多少个 \(\text{O}\)

\(p=a_i\) 的概率打出 \(\text{O}\),则有 \(p\) 的概率 \(y_i=y_{i-1}+1\);有 \(1-p\) 的概率打出的不是 \(\text{O}\),则有 \(1-p\) 的概率 \(y_i=0\)

\(E(y_i)=E(p\times (y_{i-1}+1))=p\times (E(y_{i-1})+1)\)

因为 \((x+1)^3=x^3+3x^2+3x+1\),所以还需要维护平方和。每打出 \(1\)\(\text{O}\),答案 \((x+1)^3\) 比原答案 \(x^3\) 相比多了 \(3x^2+3x+1\)

\(E({y_i}^2)=E(p\times (y_{i-1}+1)^2)=p\times (E({y_{i-1}}^2)+2\times E(y_{i-1})+1)\)

可以分别用 \({f_1}_i,{f_2}_i\) 维护 \(E(y_i),E({y_i}^2)\)

\({f_1}_{i}=p\times ({f_1}_{i-1}+1)\)

\({f_2}_{i}=p\times({f_2}_{i-1}+2\times {f_1}_{i-1}+1)\)

那么 \({ans}_i={ans}_{i-1}+p\times (3\times {f_2}_{i-1}+3\times {f_1}_{i-1}+1)\),最终的答案为 \({ans}_{n}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n;
double a[N],f1[N],f2[N],ans[N];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lf",&a[i]);
    for(int i=1;i<=n;i++){
        f1[i]=a[i]*(f1[i-1]+1);
        f2[i]=a[i]*(f2[i-1]+2*f1[i-1]+1);
        ans[i]=ans[i-1]+a[i]*(3*f2[i-1]+3*f1[i-1]+1);
    }
    printf("%.1lf\n",ans[n]);
    return 0;
} 

4. HDU 4035 Maze

题目大意:有一个树形的迷宫,有 \(N\) 个房间以及 \(N-1\) 条通道将它们连通,一开始在 \(1\) 号房间,每进入一个房间 \(i\),有 \({kill}_i\) 的概率被陷阱杀死回到房间 \(1\),有 \(s_i\) 的概率找到出口逃离迷宫,如果没有找到出口也没有被杀,那么就在与该房间相连的通道中等概率随机选一条走,求逃离迷宫所需要走的通道数的期望值。(如果不能逃离输出impossible)。\(T\leq 30,N\leq 10^4\)

Solution:

\(f_i\) 表示目前在节点 \(i\) 上的期望步数。

对于任意一个节点 \(u\)\(f_u={kill}_u f_1+s_u\times 0+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v} (f_v+1)\)(节点 \(v\) 与节点 \(u\) 相连)。

\(s_u\times 0\) 这一项省去,再把最后一项拆出来,则 \(f_u={kill}_u f_1+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v} f_v+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v} 1)\),也就是 \(f_u={kill}_u f_1+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v\in {fa_u}} f_{v}+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v\in {son_u}} f_v+(1-{kill}_u-s_u)\)

注意到:1. 对于任意一个节点 \(u\),式子中都有 \(f_1\)。 2. 如果 \(u\) 为叶子节点,那么 \(v\) 只有 \(u\) 的父亲(叶子节点没有儿子)。

可以发现,叶子节点的式子可以表示成:\(f_u=a_u\times f_1+b_u\times f_{{fa}_{u}}+c_u\),其中 \(a_u\)\(b_u\) 以及 \(c_u\) 都是常数。

\(u\)\(i\) 的父亲,将 \(f_i=a_i\times f_1+b_i\times f_u+c_i\) 代入最开始的那个式子得:

\(f_u={kill}_u f_1+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v\in {fa_u}} f_{v}+\frac{1-{kill}_u-s_u}{{deg}_u}\sum(a_i\times f_1+b_i\times f_u+c_i)+(1-{kill}_u-s_u)\)

那么:

\((1-\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum b_i)\times f_u=({kill}_u+\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum a_i)\times f_1\)
\(+\frac{1-{kill}_u-s_u}{{deg}_u}\sum\limits_{v\in{fa_u}} f_v+(1-{kill}_u-s_u+\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum c_i)\)

整理一下式子,可以得到:

\(\displaystyle f_u=\frac{({kill}_u+\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum a_i)}{1-\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum b_i}\times f_1\)
\(\displaystyle+\frac{\frac{1-{kill}_u-s_u}{{deg}_u}}{1-\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum b_i}f_{{fa}_u}+\frac{(1-{kill}_u-s_u+\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum c_i)}{1-\frac{1-{kill}_u-s_u}{{deg}_u}\times \sum b_i}\)

把这个式子和 \(f_u=a_u\times f_1+b_u\times f_{{fa}_{u}}+c_u\) 对比,可以发现,所有的式子都能和叶子节点的式子一样被表示成 \(f_u=a_u\times f_1+b_u\times f_{{fa}_{u}}+c_u\) 的形式。

这意味着我们能从叶子节点一直往上推,到根节点的时候就能得到答案。

\(f_1=a_1\times f_1+b_1\times 0+c_1\),所以 \(f_1=\frac{c_1}{1-a_1}\)\(a_1=1\) 时无解。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int t,n,m,x,y,tot,cnt,hd[N],to[N<<1],nxt[N<<1];
double k[N],s[N],a[N],b[N],c[N];
void add(int x,int y){
    to[++cnt]=y,nxt[cnt]=hd[x],hd[x]=cnt;
}
void dfs(int x,int fa){
    double num=0,ka=0,kb=0,kc=0,p=1.0-k[x]-s[x];
    a[x]=b[x]=c[x]=0;
    for(int i=hd[x];i;i=nxt[i]){
        int y=to[i];
        if(y!=fa) ++num,dfs(y,x),ka+=a[y],kb+=b[y],kc+=c[y];
    }
    if(x!=1) ++num;
    if(num==1&&x!=1) a[x]=k[x],b[x]=1.0-k[x]-s[x],c[x]=b[x];    //叶子节点 
    else a[x]=(k[x]+p/num*ka)/(1-p/num*kb),b[x]=(p/num)/(1-p/num*kb),c[x]=(p+p/num*kc)/(1-p/num*kb);    //非叶子节点 
}
signed main(){
    scanf("%lld",&t);
    while(t--){
        cnt=0,memset(hd,0,sizeof(hd));
        scanf("%lld",&n);
        for(int i=1;i<n;i++){
            scanf("%lld%lld",&x,&y);
            add(x,y),add(y,x);
        }
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&k[i],&s[i]);
            k[i]/=100,s[i]/=100;
        }
        dfs(1,0);
        if(fabs(1-a[1])<1e-9) printf("Case %lld: impossible\n",++tot);    //卡精度 
        else printf("Case %lld: %.6lf\n",++tot,c[1]/(1-a[1]));
    }
    return 0;
} 
posted @ 2020-08-12 21:02  maoyiting  阅读(742)  评论(0编辑  收藏  举报