zoukankan      html  css  js  c++  java
  • 专题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]<<'
    ';
    }
    
  • 相关阅读:
    mysq 日期相减
    说说时间观与时间管理——北漂18年(71)
    ionic之切换开关
    ionic之单选框
    SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE locks在RR模式下可以看到最新的记录
    14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读
    14.5.2.2 autocommit, Commit, and Rollback
    14.5.2 事务隔离级别
    对于唯一索引使用唯一条件搜索, InnoDB 只锁定找到的index record,不是它之前的区间
    mysql explain 解释
  • 原文地址:https://www.cnblogs.com/endlesskkk/p/15495206.html
Copyright © 2011-2022 走看看