zoukankan      html  css  js  c++  java
  • 【期望DP】

    【总览】

    【期望dp】

      求解达到某一目标的期望花费:因为最终的花费无从知晓(不可能从$infty$推起),所以期望dp需要倒序求解。

      设$f[i][j]$表示在$(i, j)$这个状态实现目标的期望值(相当于是差距是多少)。

    首先$f[n][m] = 0$,在目标状态期望值为0。然后$f = (sum f' × p) + w $,$f'$为上一状态(距离目标更近的那个,倒序),$p$为从$f$转移到$f'$的概率(则从$f'$转移回$f$的概率也为$p$),w为转移的花费。

    最后输出初始位置的$f$即可。

     

    特别的,当转移关系不成环时,期望dp可以线性递推。

    但当转移关系成环时,期望dp的最终状态相当于一个已知量,而转移关系相当于一个个方程,可以使用【高斯消元】解决。

    高斯消元期望dp的例题

    【概率dp】

      概率dp通常已知初始的状态, 然后求解最终达到目标的概率,所以概率dp需要顺序求解。

      概率dp相对简单,当前状态只需加上所有上一状态乘上转移概率即可:$f = sum f'_{i} × p_{i}$

    【例题】

    【hdu3853】Loops

      简单的期望dp题,设$f[i][j]$表示当前位置到达终点的期望体力,则$f[r][c] = 0$。

      已知每个位置不动、向下、向右的概率。设p0为当前状态下停留的概率,p1为向下的概率,p2为向右的概率,那么就从终点开始逆推:

    $$f[i][j] = p0 × f[i][j] + p1 × f[i + 1][j] + p2 × f[i][j +1] + 2$$

      dp强调根据已知推未知,发现等号右边$f[i][j]$正是我们要求的,呢么这就可以构成一个方程了。不过没有那么复杂,因为转移关系不是一个环,只要我们将右边的$f[i][j]$移到左边,再将系数除过去,等号右边就都是已知的了。

    【CODE】

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int R = 1005, C = 1005;
    const double eps = 1e-5;
    int r, c;
    double p[R][C][3];
    double f[R][C];
    
    int main(){
        while(scanf("%d%d", &r, &c) != EOF){
            memset(p, 0, sizeof p);
            memset(f, 0, sizeof f);
            for(int i = 1; i <= r; i++)
                for(int j = 1; j <= c; j++)
                    scanf("%lf%lf%lf", &p[i][j][0], &p[i][j][1], &p[i][j][2]);
            f[r][c] = 0;
            for(int i = r; i >= 1; i--)
                for(int j = c; j >= 1; j--){
                    if(i == r && j == c) continue;
                    if(fabs(1.0 - p[i][j][0]) < eps) continue;
                    f[i][j] = (p[i][j][1] * f[i][j + 1] + p[i][j][2] * f[i + 1][j] + 2.0) / (1.0 - p[i][j][0]);
                }
            printf("%.3f
    ", f[1][1]);
        }
        return 0;
    }
    View Code

    【hdu4405】AeroplaneChess

      又是一道期望dp。读题可知终点落在$n$~ $n + 5$,将它们的f全部置为$0$。

      因为有直接跳转,所以如果当前点有可以直接跳转到的点,那么这次是不用掷骰子的,因为当前期望等于目标点的期望。

      然后考虑掷色子,摇到$1, 2, 3, 4, , 6$的概率都为$frac{1}{6}$,所以$f[i] = sum_{x = 1}^{6} f[i + x] × frac{1}{6} + 1$

      这样倒序dp便可以得到期望值。

    【CODE】

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int N = 100050;
    int go[N];
    int n, m;
    double f[N];
    
    int main(){
        while(~scanf("%d%d", &n, &m), n + m){
            memset(go, -1, sizeof go);
            for(int i = 1; i <= m; i++){
                int x, y; scanf("%d%d", &x, &y);
                go[x] = y;
            }
            memset(f, 0, sizeof f);
            for(int i = n - 1; i >= 0; i--){
                if(go[i] != -1){
                    f[i] = f[go[i]];
                    continue;
                }
                f[i] = (f[i + 1] + f[i + 2] + f[i + 3] + f[i + 4] + f[i + 5] + f[i + 6]) / 6 + 1;
            }
            printf("%.4f
    ", f[0]);
        }
        return 0;
    }
    View Code

     【poj2096】收集错误

      这道题很有意思。设$f[i][j]$为收集到$i$种bug,属于$j$个子系统的期望天数,同样$f[n][s] = 0$

      考虑当前bug:

    •  属于已经收集到的$i$种,也属于已经收集到的$j$个系统,概率为$frac{i × j}{n × s}$
    •     属于已经收集到的$i$种,属于新的一套系统, 概率为$frac{i × (s - j)}{n × s}$
    •    属于新的一种,属于已经收集到的$j$个系统,概率为$frac{(n - i) × j}{n × s}$
    •     属于新的一种,属于新的系统,概率为$frac{(n - i) × (s - j)}{n × s}$

    上面顺推求出的概率,应该是等于逆推的概率的。

    其余的就很基础了。

    【CODE】

    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    using namespace std;
    
    const int N = 1005, S = 1005;
    double f[N][S];
    int n, s;
    
    int main(){
        scanf("%d%d", &n, &s);
        f[n][s] = 0.0;
        for(int i = n; i >= 0; i--){
            for(int j = s; j >= 0; j--){
                if(n * s - i * j == 0) continue;
                double c1 = (double)i * ((double)s - (double)j), c2 = ((double)n - (double)i) * (double)j, 
                       c3 = ((double)n - (double)i) * ((double)s - (double)j), c4 = (double)n * (double)s, c5 = (double)n * (double)s - (double)i * (double)j;
                f[i][j] = ((c1 * f[i][j + 1] + c2 * f[i + 1][j] + c3 * f[i + 1][j + 1] + c4) / c5);
            }
        }
        printf("%.4f
    ", f[0][0]);
        return 0;
    }
    View Code

     【poj3071】FootBall

      终于到概率dp了。设$f[i][j]$表示当前第$i$轮比赛,$j$队获胜的概率,那么他如果想获胜:

    • 首先上一轮比赛他必须获胜。
    • 然后他的对手上一轮必须获胜。
    • 他的对手只能是相邻的。

      判断相邻十分巧妙的使用了二进制:如果把所有队伍的编号都$-1$:

      从$0$开始的自然数(二进制):$0, 1, 10, 11, 100, 101, ......$

      可以发现相邻的数它们的最后一位一定相反。

      进行第一轮比赛后,相当于将相邻俩个节点替换成他们的父节点$(k >> 1)即将最后一位去掉$,此时相邻的点仍然符合规律。

      所以我们判断两队是否能比赛的标准就是:$(j >> (i - 1)) $ ^ $1 == k >> (i - 1)$

    【CODE】

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    const int N = 8;
    int n;
    double f[N][300], p[300][300];
    
    int main(){
        freopen("h.in", "r", stdin);
        while(scanf("%d", &n), n != -1){
            
            memset(p, 0, sizeof p);
            memset(f, 0, sizeof f);
            for(int i = 1; i <= (1 << n); i++){
                f[0][i] = 1;
                for(int j = 1; j <= (1 << n) ; j++)
                    scanf("%lf", &p[i][j]);
            }
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= (1 << n); j++)
                    for(int k = 1; k <= (1 << n); k++)
                        if((((j - 1) >> (i - 1)) ^ 1) == ((k - 1) >> (i - 1)))
                            f[i][j] += f[i - 1][k] * f[i - 1][j] * p[j][k];
            double ans = -1;
            int ret = 0;
            for(int i = 1; i <= (1 << n); i++)
                if(ans < f[n][i]) ans = max(ans, f[n][i]), ret = i;
            printf("%d
    ", ret);
        }
    }
    View Code
  • 相关阅读:
    就业DAY7_web服务器_tcp三次握手四次挥手,返回浏览器需要的页面http服务器
    就业DAY7_web服务器_http协议
    就业DAY6_web服务器_正则表达式
    就业DAY5_多任务_协程
    就业DAY5_多任务_进程,进程池,队列
    win10安装ubuntu系统,报错WslRegisterDistribution failed with error: 0x8007019e
    解决ubuntu与win10双系统时间不同步
    Linux常用压缩解压命令
    ubuntu添加国内源
    解决Ubuntu“下载额外数据文件失败 ttf-mscorefonts-installer”的问题 (转载)
  • 原文地址:https://www.cnblogs.com/CzYoL/p/7220088.html
Copyright © 2011-2022 走看看