zoukankan      html  css  js  c++  java
  • 概率DP入门学习QAQ

    emmmm博客很多都烂尾了。。。但是没空写。。先写一下正在学的东西好了

    概率DP这东西每次考到都不会。。听题解也是一脸懵逼。。所以决定学习一下这个东东。。毕竟NOIP考过。。。比什么平衡树实在多了QAQ

    有时间再去复习一下平衡树和KMP,AC自动机什么的,都忘光了。。

    概率DP这东东。。我也不好说啥定义(毕竟是蒟蒻嘛。。直接刷题咯)

    T1:hdu3853

    题意:一个r行c列的格子,起始点在(1,1),终点在(r,c),每一步可能的走法有:不动、向右走、向下走,每走一步花费两点魔法值,现给出在每一点三种走法的概率,求走完迷宫时所花魔法值的期望。

    这道题目可以看出是概率DP吧、、、那么我们显然知道一个事实dp[r][c]=0,不难写出DP方程:dp[i][j] = p[i][j][1]*dp[i][j] + p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2

    但是接下来的化简我理解了很久。。。之后我才明白QAQ

    下面贴出化简的关键步骤

    因为dp[i][j]-p[i][j][1]*dp[i][j]=dp[i][j]*(1-p[i][j][1])
    所以dp[i][j] = (p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2)/(1-p[i][j][1])

    下面贴上代码:

    #include<cstdio>
    using namespace std;
    double dp[1005][1005];
    double p[1005][1005][4];
    int n,m;
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)scanf("%lf%lf%lf",&p[i][j][1],&p[i][j][2],&p[i][j][3]);
        for(int i=n;i>=1;i--)
            for(int j=m;j>=1;j--){
            if(p[i][j][1]==1||(i==n&&j==m))continue;
            dp[i][j]=(p[i][j][2]*dp[i][j+1]+p[i][j][3]*dp[i+1][j]+2)/(1-p[i][j][1]);
        }printf("%.3lf
    ",dp[1][1]);
    }    

     T2:hdu4405

    有0-n个格子,初始点在0,终点是>=n,每走一步之前都要丢一次六个面的色子,标上1-6,扔到几就走几步,当然色子是等概率出现数字的,还有就是中间某一点可能和其它的一点联通,比如a和b联通,当我处于a时,就可以直接飞到b(不用耗费步数),最后问走到终点时所扔色子次数的期望。

    这题显然期望DP,显然期望DP的初始值为dp[n]=0,做期望DP要记住一点,要从距离结果状态较近的状态推到结果状态较远的状态。所以不要从dp[0]开始推哦!

    然后如果我们发现某个点能否飞到其他店那么显然dp[i]=dp[fly[i]]

    否则dp[i]=sum(d[i+j]/6)+1(1<=j<=6)

    然后暴力推就好啦

    #include<cstdio>
    #include<cstring>
    using namespace std;
    int fly[100005];
    double dp[100005];
    int n,m,x,y;
    int main(){
        while(~scanf("%d%d",&n,&m)){
            if(n==0&&m==0)break;
            memset(dp,0,sizeof(dp));
            memset(fly,0,sizeof(fly));
            for(int i=1;i<=m;i++)scanf("%d%d",&x,&y),fly[x]=y;
            for(int i=n-1;i>=0;i--){
                if(fly[i])dp[i]=dp[fly[i]];
                else for(int j=1;j<=6;j++)dp[i]+=(dp[i+j]+1)/6;
            }printf("%.4lf
    ",dp[0]);
        }
    }

    T3:poj2096

    题意:有n类bug和s个子系统,bug数量不限,且每天只能发现一个bug,要求的是当在s个子系统中发现n类bug时所需要天数的期望(平均天数)。

    emmm这道题目首先我们设置一下初始状态dp[n][s]=0,dp[i][j]表示从j个子系统中发现i个bug的状态转移到s个子系统中发现n个bug的状态的期望天数

    那么显然dp[i][j]会从以下4个地方转移:dp[i][j],dp[i+1][j],dp[i+1][j+1],dp[i][j+1]

    概率也很好算的QAQ,然后我们就能得到下面的式子:

    dp[i][j]=dp[i+1][j+1]*((n-i)*(s-j)/n/s)+dp[i][j+1]*(i*j/n/s)+dp[i+1][j]*((n-i)*j/n/s)+dp[i][j]*(i*j/n/s)+1

    因为递推式不能重复出现同一个变量,所以我们移项再化简,合并同类项得到下面的式子:

    dp[i][j]=((dp[i+1][j+1]*(n-i)*(s-j)+dp[i][j+1]*i*(s-j)+dp[i+1][j]*(n-i)*j)/(1.0*n*s)+1)/(1-(1.0*i*j/n/s));

    就刷完啦!QAQ

    #include<cstdio>
    using namespace std;
    double dp[1005][1005];
    int n,s;
    int main(){
        scanf("%d%d",&n,&s);
        for(int i=n;i>=0;i--)
            for(int j=s;j>=0;j--){
                if(i==n&&j==s)continue;
                dp[i][j]=((dp[i+1][j+1]*(n-i)*(s-j)+dp[i][j+1]*i*(s-j)+dp[i+1][j]*(n-i)*j)/(1.0*n*s)+1)/(1-(1.0*i*j/n/s));
            }
        printf("%.4lf
    ",dp[0][0]);
    }

     T4:有2^n支队,现在要进行n次比赛,并且按次序进行比赛并淘汰,胜利的队继续按次序比赛并淘汰,比如1,2,3,4进行比赛,第一轮1和2比,3和4比,假如1和3胜利了,那么第二轮1和3继续比,2,4淘汰。最后问最有可能胜利的队伍是哪一支。输出数据以-1结束

    这个题目很显然概率DP,注意:概率DP跟期望DP不一样,概率DP是正着推。0.0

    方程很水emmmm:f[i][j]+=f[i-1][j]*(f[i-1][k]*beat[j][k])

    但是我们的k有限制的QAQ因为按照顺序比赛,比如说1号队伍和3号队伍在第一局并不能比赛。

    所以我们要找到一个性质:如果j>>(i-1)==k>>(i-1)^1那么第i局j和k就是在同一组的

    然后就轻松啦QAQ

    #include<cstdio>
    #include<cstring>
    using namespace std;
    double f[1005][1005];
    double beat[1005][1005],ans;
    int n,ansnum;
    int main(){
        while(~scanf("%d",&n)&&n!=-1){memset(beat,0,sizeof(beat));
            for(int i=0;i<(1<<n);i++)for(int j=0;j<(1<<n);j++)scanf("%lf",&beat[i][j]);
            memset(f,0,sizeof(f));ans=ansnum=0;
            for(int i=0;i<(1<<n);i++)f[0][i]=1;
            for(int i=1;i<=n;i++)    
                for(int j=0;j<(1<<n);j++)
                    for(int k=0;k<(1<<n);k++){
                        if((j>>(i-1))==((k>>(i-1))^1))f[i][j]+=f[i-1][j]*(f[i-1][k]*beat[j][k]);
            }
            for(int i=0;i<(1<<n);i++)if(f[n][i]>ans)ans=f[n][i],ansnum=i;
            printf("%d
    ",ansnum+1);
        }
    }
  • 相关阅读:
    ParallelStreamAPI
    VS Code 安装后的一些配置项
    RSA算法在Python Django中的简单应用
    DLL重定向处理
    Oracle和MySQL差异总结
    Oracle中Table函数简单应用实例
    数据库开发规范
    CentOS7安装开发工具套件时报错解决方案
    安装beanstalkd队列问题——No package beanstalkd available
    CentOS7配置yum源
  • 原文地址:https://www.cnblogs.com/ghostfly233/p/7638320.html
Copyright © 2011-2022 走看看