• 专题6


    概率dp

    在比赛中会有很多题目涉及求期望或概率的题目,虽然用数学办法可能可以算出结果,但通过dp的方式求得概率或期望才是出题人所希望的。


    POJ2096 Collecting Bugs

    一个倒霉蛋每天都能收集到bug,一个软件有(s)个子系统,会产生(n)种不同的bug,每个bug属于某个子系统的概率是(1/s),属于某种分类的概率为(1/n),求发现(n)种bug且每个子系统都发现bug的天数的期望。

    如果通过正常的数学方法计算,会发现这是一个无穷级数,计算会相对繁琐。

    不过不难发现,当已经发现了(n)种bug,且每个子系统都发现bug时期望为(0),而且可以通过此来推出之前状态的期望。

    我们用(f[i][j])来表示已经发现(i)种bug,且(j)个子系统发现了bug的情况下剩余天数的期望。那么不难发现(f[n][s]=0)

    现在有以下四种情况:

    1. 发现了新的bug种类,且为新的子系统,此时(f[i][j]+=(1-i/s) imes(1-j/n) imes(f[i+1][j+1]+1))

    2. 发现了新的bug种类,但为重复的子系统,此时(f[i][j]+=(1-i/s) imes j/n imes(f[i+1][j]+1))

    3. 发现了重复的bug种类,但为新的子系统,此时(f[i][j]+=i/s imes(1-j/n) imes(f[i][j+1]+1))

    4. 发现了重复的bug种类,且为重复的子系统,此时(f[i][j]+=i/s imes j/n imes(f[i][j]+1))

    最终,(f[i][j])就等于上述式子相加,将(f[i][j])合并就能够进行递推了。

    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<iomanip>
    #define ll long long
    #define pb push_back
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    using namespace std;
    const int maxn = 1010;
    double f[maxn][maxn];
    int main()
    {
        fast;
        int n,s;
        cin>>n>>s;
        memset(f,0,sizeof(f));
        f[n][s]=0;
        for(int i=n;i>=0;i--)
        {
            for(int j=s;j>=0;j--)
            {
                if(i==n && j==s) continue;
                f[i][j]=1.0*i/n*(s-j)/s*f[i][j+1]+1.0*(n-i)/n*j/s*f[i+1][j]+1.0*(n-i)/n*(s-j)/s*f[i+1][j+1]+1.0;
                f[i][j]=f[i][j]*(1.0*n*s/(n*s-i*j));
            }
        }
        // for(int i=0;i<=n;i++)
        // {
        //     for(int j=0;j<=s;j++)
        //     {
        //         cout<<f[i][j]<<' ';
        //     }
        //     cout<<'
    ';
        // }
        cout<<fixed<<setprecision(4)<<f[0][0]<<'
    ';
    }
    

    NC210477 带富翁

    小明在玩一款带富翁游戏,这个游戏具体来说就是有(n)个奖励点,每个奖励点有一定的奖励分。一开始他站在位置(1)。每次他都会扔一个有666面的筛子,如果扔到了(x),并且小明现在站在(i)这个位置,小明就会向前进(x)步到达(i+x)这个位置。如果出现了下一步会超出(n)的情况,必须重新投掷。到达(n)即视为结束,问得分的期望为多少。

    根据上一题的思路,我们可以用(f[i])来表示在位置(i)时之后能获得分数的期望。

    1. (i leq n-6)时,(f[i]=a[i]+sumlimits_{j=i+1}^{i+6}(f[j]/6))
    2. (i>n-6)时,(f[i]=a[i]+(f[i+1]+...+f[n])/(n-i))
    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    using namespace std;
    const int maxn = 110;
    int a[maxn];
    double f[maxn]={0};
    int main()
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        f[n]=a[n];
        for(int i=n-1;i>=1;i--)
        {
            f[i]=a[i];
            int num=min(i+6,n)-i;
            for(int j=i+1;j<=min(i+6,n);j++)
            {
                f[i]+=f[j]/num;
            }
        }
        cout<<fixed<<setprecision(7)<<f[1]<<'
    ';
    }
    

    NC210481 筛子游戏

    题目描述不过多赘述,具体看链接。

    首先我们可以通过三重循环将所有可能的和的概率统计出来,这里用(p[k])来表示分数加上(k)的概率。另外,分数归零用(p[0])表示。

    (f[i])来表示当前分数为(i)时需要次数的期望。可以比较容易地得出(f[i]=sumlimits_{k=3}^{18}f[i+k] imes p[k]+f[0] imes p[0]+1)

    但这个式子并不能直接进行递推,因为每个式子中都包含(f[0]),而(f[0])恰好是我需要的结果。

    如果每个结果都与(f[0])有关,那我们不妨设(f[i]=A[i] imes f[0]+B[i])。此时(A[n]=0,B[n]=0)

    代入(f[i])原式可以得到:

    (A[i]=sumlimits_{k=3}^{18}A[i+k] imes p[k]+p[0])

    (B[i]=sumlimits_{k=3}^{18}B[i+k] imes p[k]+1)

    由此递推式,我们可以求得(A[i])(B[i]),而(f[0]=B[0]/(1-A[0])),这样就可以求得答案了。

    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    using namespace std;
    const int maxn = 510;
    int n;
    int k1,k2,k3;
    double p[50]={0};
    double f[maxn]={0};
    double A[maxn]={0};
    double B[maxn]={0};
    int a,b,c;
    int main()
    {
        cin>>n>>k1>>k2>>k3>>a>>b>>c;
        for(int i=1;i<=k1;i++)
        {
            for(int j=1;j<=k2;j++)
            {
                for(int m=1;m<=k3;m++)
                {
                    if(i==a && j==b && m==c) continue;
                    p[i+j+m]++;
                }
            }
        }
        for(int i=1;i<=18;i++)
        {
            p[i]=p[i]/(k1*k2*k3);
        }
        p[0]=1.0/(k1*k2*k3);
        for(int i=n;i>=0;i--)
        {
            for(int j=3;j<=(k1+k2+k3);j++)
            {
                A[i]+=p[j]*A[i+j];
                B[i]+=p[j]*B[i+j];
            }
            A[i]+=p[0];
            B[i]+=1;
        }
        f[0]=B[0]/(1-A[0]);
        cout<<fixed<<setprecision(7)<<f[0]<<'
    ';
    }
    

    NC210487 食堂

    我们可以用(f[i][j])来表示队伍中有(i)个人,吉吉国王处于前(j)个位置处时,在关门前排在(k)​位之前的概率。这样我们可以写出状态转移方程。

    [f[i][j]= egin{equation} egin{cases} p_1f[i][j]+p_2f[i][i]+p_4 & j=1 \ p_1f[i][j]+p_2f[i][j-1]+p_3f[i-1][j-1]+p_4 & 2leq jleq k \ p_1f[i][j]+p_2f[i][j-1]+p_3f[i-1][j-1] & k<jleq i end{cases} end{equation} ]

    和上一题出现了类似的情况在(j=1)的情况下,(f[i][1])(f[i][i])都是未知,因此公式需要进行处理。

    首先将(f[i][j])合并同类项,得到:

    [f[i][j]= egin{equation} egin{cases} frac{p_2f[i][i]}{1-p_1}+frac{p_4}{1-p_1} & j=1 \ frac{p_2f[i][i]}{1-p_1}+frac{p_3f[i-1][j-1]}{1-p_1}+frac{p_4}{1-p_1} & 2leq jleq k \ frac{p_2f[i][i]}{1-p_1}+frac{p_3f[i-1][j-1]}{1-p_1} & k<jleq i end{cases} end{equation} ]

    (k_2,k_3,k_4)分别等于(frac{p_2}{1-p_1},frac{p_3}{1-p_1},frac{p_4}{1-p_1}),然后我们可以表示出所有的常数项(在这里将(f[i-1][j-1])看作常数:

    [c[i]= egin{equation} egin{cases} k_4&j=1 \ k_3f[i-1][j-1]+k_4&2leq j leq k \ k_3f[i-1][j-1]&k<jleq i end{cases} end{equation} ]

    由此公式可以列出:

    [egin{equation} egin{cases} f[i][1]=k_2f[i][i]+c[1] \ f[i][2]=k_2f[i][1]+c[2] \ ...\ f[i][i]=k_2f[i][i-1]+c[i] end{cases} end{equation} ]

    现在,我们可以通过反复代入求出(f[i][i]),进而求出(f[i][1]),然后就能够递推出所有情况了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 2010;
    int n,m,k;
    double p1,p2,p3,p4;
    double f[maxn][maxn]={0};
    double p[maxn];
    double c[maxn];
    int main()
    {
        cin>>n>>m>>k;
        cin>>p1>>p2>>p3>>p4;
        double k2=p2/(1-p1);
        double k3=p3/(1-p1);
        double k4=p4/(1-p1);
        f[1][1]=p4/(1-p1-p2);
        p[0]=1;
        for(int i=1;i<=n;i++)
        {
            p[i]=p[i-1]*k2;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                if(j<=k) c[j]=f[i-1][j-1]*k3+k4;
                else c[j]=f[i-1][j-1]*k3;
            }
            double tmp=0;
            for(int j=1;j<=i;j++)
            {
                tmp+=p[i-j]*c[j];
            }
            f[i][i]=tmp/(1-p[i]);
            f[i][1]=k2*f[i][i]+k4;
            for(int j=2;j<i;j++)
            {
                f[i][j]=k2*f[i][j-1]+c[j];
            }
        }
        cout<<fixed<<setprecision(5)<<f[n][m]<<'
    ';
    }
    
  • 相关阅读:
    【操作系统】第九章 虚拟内存
    【计网】第七章
    信息安全数学基础整理笔记
    深入浅出数据分析 笔记总结
    ERROR: Error cloning remote repo 'origin'
    Docker 操作
    centos 安装 git
    CentOS 7 安装 JAVA环境(JDK 1.8)
    如何删除 容器里的软件(彻底)
    Docker 部署 Jenkins :通过SSH配置Linux宿主机为slave节点,运行外部环境
  • 原文地址:https://www.cnblogs.com/endlesskkk/p/15495206.html
Copyright © 2020-2023  润新知