概率dp专题

概率dp

期望的定义

一个期望 e[x] 表示对于一个状态其达到目标状态的平均值(姑且这样简单的认为)

基本套路

例如我们要求一个状态i

例题

Collecting Bugs

一个人受雇于某公司要找出某个软件的bugs和subcomponents,这个软件一共有n个bugs和s个subcomponents,每次他都能同时随机发现1个bug和1个subcomponent,问他找到所有的bugs和subcomponents的期望次数。(s<=1000,n<=1000)

思路

我们发现dp的最终的状态为dp[s][n]。
dp[i][j]=dp[i1][j](si+1)/sj/n+dp[i][j1]i/s(nj+1)/n+dp[i1][j1](si+1)/s(nj+1)/n+dp[i][j]i/sj/n
由于正着写有些麻烦我们可以反过来写。
用E(i,j)表示他找到了i个bugs和j个subcomponents,表示离找到n个bugs和s个subcomponents还需要的期望次数。

int solve(){  
    int n,s;  
    while(~scanf("%d%d",&n,&s)){  
        memset(e,0,sizeof(e));  
        double f=1.0*n*s;  
        for(int i=n;i>=0;i--)  
        for(int j=s;j>=0;j--){  
            if(i==n&&j==s)continue;  
            double p=1,p0=1-i*j/f;
            p+=e[i][j+1]*i*(s-j)/f;
            p+=e[i+1][j]*j*(n-i)/f;
            p+=e[i+1][j+1]*(n-i)*(s-j)/f;
            e[i][j]=p/p0;  
        }printf("%.4lf\n",e[0][0]);  
    }  
}  

One Person Game

有三个骰子,分别有 k1,k2,k3 面。每次掷骰子,如果三个面分别为 a,b,c 则分数重置为 0 ,否则加上三个骰子的分数之和。当分数大于n时结束。求游戏的期望步数。(初始分数为 0,k<6 )

思路

对于每一个 e[i] = (e[i+k]pk)+e[0]p0+1
但是我们发现每一项都有p[0]那么我们一定可以通过迭代得到一个只有p[0]的方程
e[i]=A[i]e[0]+B[i]
= (pkA[i+k]e[0]+pkB[i+k]) + e[0]p0+1
= (pkA[i+k])+p0)e[0] + (pkB[i+k])+1
A[i] = (pkA[i+k])+p0)
B[i] = (pkB[i+k])+1
那么 e[0]=B[0]/(1A[0])

const int M=1e3+5;
double A[M],B[M],p[M];
void solve(){
    int n,k1,k2,k3,c1,c2,c3;
    memset(A,0,sizeof(A));memset(B,0,sizeof(B));memset(p,0,sizeof(p));
    scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&c1,&c2,&c3);
    p[0]=1.0/k1/k2/k3;
    for(int a=1;a<=k1;a++)
        for(int b=1;b<=k2;b++)
            for(int c=1;c<=k3;c++)
                if(a!=c1||b!=c2||c!=c3)
                    p[a+b+c]+=p[0];//先对于每一个k求出它的p[k]

    for(int i=n;i>=0;i--){
        A[i]=p[0];B[i]=1;
        for(int j=3;j<=k1+k2+k3;j++)
            A[i]+=A[i+j]*p[j],
            B[i]+=B[i+j]*p[j];
    }
    printf("%.15lf\n",B[0]/(1-A[0]));
}

Activation

有n个人排队等着在官网上激活游戏。Tomato排在第m个。
对于队列中的第一个人。有一下情况:
1、激活失败,留在队列中等待下一次激活(概率为p1)
2、失去连接,出队列,然后排在队列的最后(概率为p2)
3、激活成功,离开队列(概率为p3)
4、服务器瘫痪,服务器停止激活,所有人都无法激活了(概率为p1)。
求服务器瘫痪时Tomato在队列中且的位置<=k的概率

思路

<1> e[s][1]=p2e[s][s]+p4;
<2> e[s][r<=k](1p1)=p2e[s][r1]+p3e[s1][r1]+p4;
<3> e[s][r>k](1p1)=p2e[s][r1]+p3e[s1][r1];
但是我们发现对于 e[s][1] 我们必须先求出 e[s][s]
对于 e[s][1] 我们又必须先求出 e[s][s1]
对于这样的的线性成环的e的关系
我们只能一个一个迭代最后求出e[i][i]之后再得到其他答案
简化一下得
<1> e[i][1]=p2e[i][i]+c[1];
<2> e[i][j]=p2e[i][j1]+c[j]+p4;
<3> e[i][j]=p2e[i][j1]+c[j];
e[i][2]<e[i][1]<e[i][i]<e[i][i1]
e[i][i]
e[i][i]=c[i]+e[i][i1]
e[i][1]=c[1]+p2e[i][i]
e[i][2]=c[2]+p2(c[1]+e[i][1])c[1]p2(i1)
e[i][2]=c[2]+p2c[1]+p2e[i][1]
e[i][2]=c[2]+p2(c[1]+p2e[i][i])
e[i][2]=c[1]p2+c[2]+p2p2e[i][i]
……
于是得到了 e[i][i]=ix=1(c[x]p[ix])+e[i][i]p[i](p[x]p2x

void solve(){
    if(p4<EPS){puts("0.00000");return;}
    p2/=(1-p1),p4/=(1-p1),p3/=(1-p1);
    //方便起见我们直接把p1移项。
    p[0]=1.0;//p[i]=p2^i
    for(int i=1;i<=n;i++)p[i]=p2*p[i-1];

    e[1][1]=p4/(1-p2);//e[1][1]=p2*e[1][1]+p4;

    c[1]=p4;

    bool cur=1;

    for(int i=2;i<=n;i++){
        cur=!cur;

        //预处理所有的常数 
        for(int j=2;j<=i;j++){
            c[j]=p3*e[!cur][j-1];
            if(j<=k)c[j]+=p4;
        }

        e[cur][i]=0;//先求出e[i][i] 

        for(int j=1;j<=i;j++)e[cur][i]+=c[j]*p[i-j];

        e[cur][i]/=(1-p[i]);

        e[cur][1]=p2*e[cur][i]+c[1];//得到e[i][1] 

        for(int j=2;j<i;j++)e[cur][j]=p2*e[cur][j-1]+c[j];//迭代即可 

    }
    printf("%.5lf\n",e[cur][m]);
}

Maze

有n个房间,由n-1条隧道连通起来,实际上就形成了一棵树,
从结点1出发,开始走,在每个结点i都有3种可能:
1.被杀死,回到结点1处(概率为ki)
2.找到出口,走出迷宫 (概率为ei)
3.和该点相连有m条边,随机走一条
求:走出迷宫所要走的边数的期望值。

思路

e[i]=k[i]e[1]+res/me[f]+res/me[son]+res
于是我们用A[i]记录e[1]的系数,B[i]记录e[f]的系数,C[i]记录常数即可

void dfs(int x,int pre){
    double res=1-e[x]-k[x],self=0,q;
    int m=G[x].size();
    q=res/m;
    A[x]=k[x];
    B[x]=q;
    C[x]=res;
    for(int i=0;i<m&&res>EPS;i++){
        int y=G[x][i];
        if(y==pre)continue;
        dfs(y,x);
        A[x]+=q*A[y];
        C[x]+=q*C[y];
        self+=q*B[y];
    }
    A[x]/=1-self;
    B[x]/=1-self;
    C[x]/=1-self;
}
int main(){
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        scanf("%d",&n);
        int x,y;
        for(int i=0;i<M;i++)G[i].clear();
        for(int i=1;i<n;i++){
            scanf("%d %d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        for(int i=1;i<=n;i++){
            scanf("%d %d",&x,&y);
            k[i]=0.01*x;e[i]=0.01*y;
        }dfs(1,0);
        cout<<"Case "<<cas<<": ";
        if(1-A[1]<EPS)puts("impossible");
        else printf("%.6f\n",C[1]/(1-A[1]));
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值