【专题总结】概率&期望DP

本文总结了概率与期望动态规划问题的解决方法,包括概率类问题的dp[i]=σ(p[j]*dp[j])转移和期望类问题的dp[i]=σ(p[j]*(dp[j]+cost))转移。在遇到状态间关系复杂的情况,通过建立方程组使用高斯消元或人肉消元来解决。文中列举了若干典型题目,帮助读者理解和应用这些策略。
摘要由CSDN通过智能技术生成

下面是对一系列求概率&期望问题的总结:


问题的形式一般是求一个事件发生的概率或者期望,而概率和期望的求解形式是有些区别的,一般方法dp&记忆化搜索,特殊情况可能用到人肉消元 & 高斯消元


如果是概率类的问题,那么求的方法一般是利用这个事件之前的概率,dp[i]的含义是i事件发生的概率(当然这里不一定是一维数组,写一维是为了简单起见,思路是一样的,下同)

大概的流程是 dp[i] = sigma(p[j] * dp[j]),j是可能转移到i的事件

即当前事件发生的概率是通过“可能转移到当前事件的事件”推过来的

初始状态的dp[i] = 1


而对于期望类的问题,求的方法一般是利用这个事件之后的概率,dp[j]表示从j状态转移到终止状态的期望

那么要求的其实就是从起始状态转移到终止状态的期望,即dp[s]

转移和求概率相反,是从“当前事件可能转移到的后续事件”推到当前事件,也就是倒推的一个过程

也就是dp[i] = sigma(p[j] * (dp[j] + cost)),j是i的后续状态,由于是求期望,所以要加上转移所消耗的步数cost

初始状态的话就是终止状态的dp[t] = 0


这样一般情况的概率&期望dp都可以解决了,最后会放一些这类可以直接解决的问题。


但是很多时候直接dp是解决不了问题的,为什么呢?因为状态之间的关系可能会比较复杂

比如求概率的时候,你在求某一个状态的时候可能无法确定所有需要知道的状态的概率。比如A可以转移到B,B也可以转移到A,那么求A的概率的时候需要知道B的概率,求B的概率的时候需要知道A的概率,这下就两个都求不出来了。


解决的方法就是先设这些概率为变量,然后建立相互之间关系的方程,最后消元得出答案

大概思路就是:每个转移方程可以构成一个包含方程,变量为各个状态的概率,这样就是n个未知数n个方程,理论上可以用高斯消元求出每个未知数

那么这种转移方程嵌套的问题也就能解决了


之前说过还有一种方法叫做人肉消元:这种方法是在高斯消元复杂度过高时候或者变量间关系不那么复杂时候运用的方法,比高斯消元能更快的得到答案。

具体是怎么搞呢?一般dp方程的形式是这样的:

dp[i] = sigma(A * dp[j]) + B * dp[i] + C

这里A,B,C都是常数,然后j是除了i之外的其他转移状态

我们发现,如果知道了所有的dp[j],那么dp[i] = (sigma(A * dp[j]) + C) / (1-B) 就解出来了

那问题就转换成怎么在之前所说转移嵌套的情况下得到dp[j]们

我们把某一个dp状态(比如dp[0])作为变量,使得每个dp状态的表示形式变成这样:

dp[i] = A*dp[0] + B * dp[i] + C

这样就可以从dp[0]的后续状态们开始推出所有的状态的表示,然后用已知数值的状态(求概率时是初始状态,求期望时是末尾状态)就可以轻松得到dp[0],那么根据这个表示法所有状态都可以求出来了

剩下要做的就是怎么维护A,B,C这三个常数了。这个在基本转移方程的基础上只要用这种表达形式稍微写一下很容易就能得出写法,就是在思维的过程可能会相对较多一些。


由于这个方法实质是通过人脑构造的方法进行消元,因此我称之为”人肉消元“(笑)

这种方法非常优美相比高斯消元也很快,但从做题速度的角度来说,思维的速度和敲模板的速度还是见仁见智吧。


下面给一些这类的题目:


高斯消元:

HDU 4418 Time Travel

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<math.h>
using namespace std;

#define CLR(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
const int MAXN=220;
double a[MAXN][MAXN],x[MAXN];//方程的左边的矩阵和等式右边的值,求解之后x存的就是结果
int equ,var;//方程数和未知数个数

int Gauss()
{
    int i,j,k,col,max_r;
    for(k=0,col=0;k<equ&&col<var;k++,col++)
    {
        max_r=k;
        for(i=k+1;i<equ;i++)
          if(fabs(a[i][col])>fabs(a[max_r][col]))
            max_r=i;
        if(fabs(a[max_r][col])<eps)return 0;
        if(k!=max_r)
        {
            for(j=col;j<var;j++)
              swap(a[k][j],a[max_r][j]);
            swap(x[k],x[max_r]);
        }
        x[k]/=a[k][col];
        for(j=col+1;j<var;j++)a[k][j]/=a[k][col];
        a[k][col]=1;
        for(i=0;i<equ;i++)
          if(i!=k)
          {
              x[i]-=x[k]*a[i][k];
              for(j=col+1;j<var;j++)a[i][j]-=a[k][j]*a[i][col];
              a[i][col]=0;
          }
    }
    return 1;
}
const int N = 111;
const int M = 211;
int nn,m,s,t,d,n;
double sum;
double p[M];
int has[M],vis[M],k;

int bfs(int u)
{
    CLR(has, -1);
    CLR(a, 0);
    CLR(vis ,0);
    int v,flg = 0;
    queue<int> q;
    q.push(u);
    k = 0 ;
    has[u] = k ++;
    while(!q.empty()){
        u = q.front();
        q.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        if(u == t || u == n-t)
        {
            a[has[u]][has[u]] = 1;
            x[has[u]] = 0;
            flg = 1;
            continue;
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值