期望与概率DP专题/高斯消元

高斯消元


总结:概率和期望问题的求解都要依靠递推式,概率问题按照递推式进行dp,期望问题根据递推式化简的难易程度通过化简再dp或高斯消元求解,该类问题大部分仍属于各个类型的dp求解问题

2018.9.3-概率与期望dp学习

Discovering Gold

一排1到n的格子,每个格子上有黄金 ai ,你最开始在 1 号,每一次投骰子决定到哪一个格子,超出1~n范围则重新投掷,你到了哪个格子就得到哪个格子的金币,问最终在n 能得到金币的期望

#include<stdio.h>
#include<string.h>
const int MAX=1e2+5;
int a[MAX];
double dp[MAX];
int t,n;
int main()
{
    scanf("%d",&t);
    for(int tc=1;tc<=t;++tc)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        dp[1]=1.0;
        for(int i=1;i<n;++i)
        {
            for(int j=1;j<=6&&i+j<=n;++j)
            {
                int tmp=6;
                if(i+6>n) tmp=n-i;
                dp[i+j]+=dp[i]/tmp;
            }
        }
        double ans=0;
        for(int i=1;i<=n;++i) ans+=dp[i]*a[i];
        printf("Case %d: %f\n",tc,ans);
    }
    return 0;
}

HDU3366 Passage

有n扇门和m枚金币,每扇门后有3种可能:1.出路概率p,2.另一扇门概率1-p-q,3.失去一枚金币并且出现另一扇门概率q,门可选择,求在最优选择情况下找到出路得概率

根据p/q从大到小对每扇门排序即为最优策略,注意q可能的值为0,需eps处理

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAX=1e3+5;
const double eps=1e-16;
double dp[MAX][15],ans;
int t,n,m;
struct P
{
    double p,q;
}a[MAX];
inline bool cmp(P& x,P& y)
{
    return (x.p+eps)/(x.q+eps)>(y.p+eps)/(y.q+eps);
}
int main()
{
    scanf("%d",&t);
    for(int tc=1;tc<=t;++tc)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i) scanf("%lf%lf",&a[i].p,&a[i].q);
        sort(a,a+n,cmp);
        dp[0][m]=1.0;ans=0;
        for(int i=0;i<n;++i)
        {
            for(int j=m;j>=0;--j)
            {
                ans+=dp[i][j]*a[i].p;
                if(i<n-1&&j>=1)dp[i+1][j-1]+=dp[i][j]*a[i].q;
                if(i<n-1)dp[i+1][j]+=dp[i][j]*(1.0-a[i].p-a[i].q);
            }
        }
        printf("Case %d: %.5f\n",tc,ans);
    }
    return 0;
}

Island of Survival

小岛上有man\tiger\deer三种生物,每天都有两种相遇:1.tiger-man  2.tiger-tiger  3.tiger-deer  4.man-deer/deer  5.deer-deer

求man活下来的概率,当且仅当tiger==0。:本题求的是man能活下来的最终概率而不是某种情况下仍存活的概率,题中3\4\5种情况只能延长man的存活时间,而不能影响man的存活概率,因此只需要对1\2情况进行讨论。

#include<stdio.h>
#include<string.h>
const int MAX=1e3+5;
double dp[MAX];
int t,n,m;
int main()
{
    scanf("%d",&t);
    for(int tc=1;tc<=t;++tc)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        if(n==0) {printf("Case %d: 1\n",tc);continue;}
        if(n%2==1) {printf("Case %d: 0\n",tc);continue;}
        dp[n]=1;
        for(int i=n;i>=2;i-=2) dp[i-2]=dp[i]*(i-1)/(i+1);
        printf("Case %d: %.6f\n",tc,dp[0]);
    }
    return 0;
}

Bag of mice

有n只白鼠m只黑鼠,小D和小P二人随机各抓1只并且小D先手,小P抓完后会有1只小鼠出逃,求小D先抓到白鼠的概率

与上题相似,抓到白鼠则游戏结束,只需讨论2人都抓不到的概率和2种小鼠随机出逃的概率,答案为各种情况下抓到白鼠的概率之和:  本题我的解法中用eps忽略不会到达的情况,需要注意eps精度的设置,题中精度偏差不超过1e-9,eps=1e-11可AC

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAX=1e3+1;
const double eps=1e-11;
double dp[MAX][MAX];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    dp[n][m]=1;
    double ans=0,tmp;
    for(int i=n;i>0;--i)
    {
        for(int j=m;j>=0;--j)
        {
            if(dp[i][j]<eps) continue;
            ans+=dp[i][j]*i/(i+j);
            if(j>=2)tmp=dp[i][j]*j*(j-1)/(i+j)/(i+j-1);
            if(i>=1&&j>=2)dp[i-1][j-2]+=tmp*i/(i+j-2);
            if(j>=3)dp[i][j-3]+=tmp*(j-2)/(i+j-2);
        }
    }
    printf("%.9f\n",ans);
    return 0;
}

Scout YYF I

一维路径上有N个地雷,有2种情况:1.走一步概率p,2.走两步概率1-p,求从1走到MAX不踩到雷得概率(n<=10,MAX=1e8)

由于地雷少且路径长,考虑矩阵快速幂优化,区间状态转移矩阵为        \begin{vmatrix} 0 &1 \\ q&p \end{vmatrix}*\begin{bmatrix} f(n)\\f(n+1) \end{bmatrix}= \begin{bmatrix} f(n+1)\\ f(n+2) \end{bmatrix}

把地雷看作区间间断点,假设 f(n+1)为间断点f(n+2)=f(n)*q,走两步到达,进行区间概率统计即可

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[11],n;
double p,q;
struct Mat
{
    double m[2][2];
    Mat(){memset(m,0,sizeof(m));}
    inline void init()
    {
        m[0][0]=0;m[0][1]=1;m[1][0]=q;m[1][1]=p;
    }
};
inline Mat multi(const Mat &a,const Mat &b)
{
    Mat c;
    for(int i=0;i<2;++i)
    for(int j=0;j<2;++j)
    for(int k=0;k<2;++k)
    c.m[i][j]+=a.m[i][k]*b.m[k][j];
    return c;
}
Mat pow(Mat &a,int k)
{
    Mat b;
    for(int i=0;i<2;++i) b.m[i][i]=1;
    while(k)
    {
        if(k&1) b=multi(b,a);
        a=multi(a,a);
        k>>=1;
    }
    return b;
}
double ans;
int main()
{
    while(~scanf("%d%lf",&n,&p))
    {
        q=1.0-p;
        for(int i=0;i<n;++i) scanf("%d",&a[i]);
        sort(a,a+n);
        n=unique(a,a+n)-a;
        if(a[0]==1) {printf("%.7f\n",0.0);continue;}
        Mat m1;m1.init();
        ans=pow(m1,a[0]-1).m[0][1]*q;
        for(int i=0;i<n-1;++i)
        {
            m1.init();
            ans=ans*pow(m1,a[i+1]-a[i]-1).m[0][1]*q;
        }
        printf("%.7f\n",ans);
    }
    return 0;
}

Batting Practice

每次投1个球投不中概率为p,连中n球或连失m球则停止,求投球次数的期望

关键词:马尔可夫链、正向推概率反向推期望

E(i,0)=(E(i+1,0)+1)*(1-p)+(E(0,1)+1)*p

E(0,j)=(E(0,j+1)+1)*p+(E(1,0)+1)*(1-p)

E(n,0)=E(0,m)=0

q=1-p,a=1-q^{n-1},b=(1-p^{m-1})

1.      E(0,0)=(E(1,0)+1)*q+(E(0,1)+1)*p

2.     E(1,0)=(1-p^{n-1})E(0,1)+\sum_{i=0}^{n-2}p^i ->E(1,0)=aE(0,1)+b/q

3.     E(0,1)=(1-(1-p)^{m-1})E(1,0)+\sum_{i=0}^{m-2}(1-p)^i->E(0,1)=bE(1,0)+a/p

由23得  E(1,0)=\frac{ab/q+a/p}{1-ab},E(0,1)=\frac{ab/p+b/q}{1-ab},代入1

#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int MAX=51;
const double eps=1e-8;
int t,n,m;
double p,q;
int main()
{
    scanf("%d",&t);
    for(int tc=1;tc<=t;++tc)
    {
        scanf("%lf%d%d",&p,&n,&m);
        q=1.0-p;
        if(p<eps||q<eps) {printf("Case %d: %.8f\n",tc,p<eps?n*1.0:m*1.0);continue;}
        double a=1.0-pow(q,n-1),b=1.0-pow(p,m-1);
        double e10=(a*b/q+a/p)/(1.0-a*b),e01=(a*b/p+b/q)/(1.0-a*b);
        printf("Case %d: %.8f\n",tc,e10*q+e01*p+1.0);
    }
    return 0;
}

ZOJ 3329 One Person Game

有三个骰子,分别有K1,K2,K3个面,点数都在[1,K1]\[1,K2]\[1,K3]之间,同时掷出三个骰子:若点数分别为a,b,c则sum=0;否则sum+=点数之和,当sum>n结束,求掷骰子次数的期望

E(i)= \sum_{x=3}^{K1+K2+k3}E(i+x)p(x)+E(0)p0+1

同上,概率公式中有环,每一个E[i]都可以转化到E[0],不妨设E[i]=a[i]E[0]+b[i];代入迭代化简得

a[i]=\sum_{x=3}^{K1+K2+K3}a[i+x]+p0           b[i]=\sum_{x=3}^{K1+K2+K3}b[i+x]+1

问题化简为求a[0]b[0]

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAX=5e2+1;
const double eps=1e-8;
double p[MAX],pa[MAX],pb[MAX],p0;
int t,n,K1,K2,K3,a,b,c;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(p,0,sizeof(p));
        memset(pa,0,sizeof(pa));
        memset(pb,0,sizeof(pb));
        scanf("%d%d%d%d%d%d%d",&n,&K1,&K2,&K3,&a,&b,&c);
        p0=1.0/K1/K2/K3;
        for(int i=1;i<=K1;++i)
        for(int j=1;j<=K2;++j)
        for(int k=1;k<=K3;++k)
            p[i+j+k]+=p0;
        p[a+b+c]-=p0;
        for(int i=n;i>=0;--i)
        {
            pa[i]+=p0;
            pb[i]+=1;
            for(int j=3;j<=K1+K2+K3&&i+j<=n;++j)
            {
                pa[i]+=pa[i+j]*p[j];
                pb[i]+=pb[i+j]*p[j];
            }
        }
        printf("%.8f\n",pb[0]/(1.0-pa[0]));
    }
    return 0;
}

HDU 4336 Card Collector

买魔法士干脆面收集袋中卡牌,共有n张卡牌,袋中有i卡牌得概率为pi,\sum {}pi<=1,求买干脆面的期望

n张卡牌形成1<<n种状态,考虑通过状态压缩列状态转移方程:

E[i]=E[i]*ps+\sum_{j=0}^{j<n}(i\&1<<j)E[i]*pj+\sum_{j=0}^{j<n}(!i\&1<<j)E[i]*pj+1ps=1-\sum_{j=0}^{j<n}pi

p1=\sum_{j=0}^{j<n}(i\&1<<j)pj  ,  p2=\sum_{j=0}^{j<n}(!i\&1<<j)pj+1,化简得

E[i]=p2/(1.0-ps-p1)

根据递推式反向求期望

#include<bits/stdc++.h>
using namespace std;
const int MAX=1<<20|1;
double dp[MAX],p[21],ps,p1,p2;
int n;
int main()
{
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));ps=1.0;
        for(int i=0;i<n;++i) {scanf("%lf",&p[i]);ps-=p[i];}
        for(int i=(1<<n)-2;i>=0;--i)
        {
            p1=p2=0;
            for(int j=0;j<n;++j)
            {
                if(i&1<<j) p1+=p[j];
                else p2+=dp[i|1<<j]*p[j];
            }
            p2+=1.0;
            dp[i]=p2/(1.0-p1-ps);//1<<n-1,1.0-p1-ps==0
        }
        printf("%.4f\n",dp[0]);
    }
    return 0;
}

HDU4870 Rating

高斯消元

有两个账号,每次选取一个分数最低的账号进行游戏,有p得概率得1分,1-p得概率失2分,最低0分,求1个号到达20的期望

由题意得,两个号的分差必定不超过1,当一个号为20分时,另一个为19

E[i]=E[i+1]*p+E[i-2]*q+1

按递推式建立矩阵,套用高斯消元函数,得到解集x

x[i]表示从i分到20分的期望,所以答案即为x[0]*2-x[19]:两倍的0~20减去19~20

该题的递推式也可以通过换元s[i]=E[i]-E[i-1]进行化简

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e2+5;
const double eps=1e-11;
double a[MAX][MAX],x[MAX],p,q;
int equ,var,n=20;
bool 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.0;
        for(i=0;i<equ;++i)
            if(i!=k)
            {
                x[i]-=x[k]*a[i][col];
                for(j=col+1;j<var;++j) a[i][j]-=a[k][j]*a[i][col];
                a[i][col]=0.0;
            }
    }
    return 1;
}
int main()
{
    equ=var=n;
    while(~scanf("%lf",&p))
    {
        memset(a,0,sizeof(a));
        q=1.0-p;
        a[0][0]=p;a[0][1]=-p;a[0][n]=x[0]=1.0;
        a[1][0]=-q;a[1][1]=1.0;a[1][2]=-p;a[1][n]=x[1]=1.0;
        for(int i=2;i<n;++i)
        {
            a[i][i-2]=-q;
            a[i][i]=1.0;
            a[i][i+1]=-p;
            a[i][n]=x[i]=1.0;
        }
        gauss();
        printf("%.6f\n",x[0]*2-x[19]);
    }
    return 0;
}

ZJUT 1423 地下迷宫

ZJUT 1317 掷飞盘

HDU 4418 Time travel

 

转载

对离散型随机变量x,其概率为p,有E[x]=\sum pi*xi

对随机变量A、B,有 E[ax+by]=aE[x]+bE[y]

第二条式子是今天的主角,他表明了期望有线性的性质,简单理解就是期望之间可根据关系,简单运算(不严谨的理解)。 这就为我们解决一个期望问题,不断转化为解决另外的期望问题,最终转化到一个已知的期望上。

举一个求期望最简单的例子,见下图。

假设有个人在 1号节点处,每一分钟他会缘着边随机走到一个节点或者在原地停留,问他走到4号节点需要平均几分钟?

这是个简单的期望问题,我们用Ei(i=1,2,3,4) 表示从i号节点走到4号节点的数学期望值。根据题意对1号节点有

E1=(1/3)*E1+(1/3)*E2+(1/3)*E3+1 ①

表示 他下一分钟可以走到2或者3或在原地1,每个可能概率是1/3 ,注意是下一分钟,故要加上1.

同理我们对节点2,3同样可以列出

E2=(1/3)*E1+(1/3)*E2+(1/3)*E4+1 ②

E3=(1/3)*E1+(1/3)*E3+(1/3)*E4+1 ③

那E4等于多少呢? 很明显E4=0 ④,因为他就是要到点4

这样上面1234式其实就是组成了一组方程组,解方程组就可得出E1!!,用高斯消元,复杂度是O(n^3)

从上述例子,我们可总结出如何解决期望类问题,根据题意,表示出各个状态的期望(上例的Ei,1234),根据概率公式,列出期望之间的方程,解方程即可。

下面看用上述思路如何解决一道题(poj2096)

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

我们用E(i,j)表示他找到了i个bugs和j个subcomponents,离找到n个bugs和s个subcomponents还需要的期望次数,这样要求的就是E(0,0),而E(n,s)=0,对任意的E(i,j),1次查找4种情况,没发现任何新的bugs和subcomponents,发现一个新的bug,发现一个新的subcomponent,同时发现一个新的bug和subcomponent,用概率公式可得:

E(i,j)=1+(i*j/n/s)*E(i,j)+(i*(s-j)/n/s)E(i,j+1)+((n-i)*j/n/s)*E(i+1,j)+(n-i)*(s-j)/n/s*E(i+1,j+1);

这样根据边界就可解出所有的E(i,j),注意因为当我们找到n个bugs和s个subcomponents就结束,对i>n||j>s均无解的情况,并非期望是0.(数学上常见问题,0和不存在的区别)

那这题是否也是要用高斯消元呢? 用高斯消元得话复杂度是O(n^3),达到10^18 根本是不可解的!!

但其实,注意观察方程,当我们要解E(i,j)的话就需要E(i+1,j),E(I,j+1),E(i+1,j+1), 一开始已知E(n,s),那其实只要我们从高往低一个个解出I,j就可以了! 即可根据递推式解出所有的E(I,j) 复杂度是O(n),10^6 ,完美解决。

从上面这道题,我们再次看到了解决期望问题的思路,而且是用到了递推解决问题,其实可递推的原因,当我们把各个状态当成是一个个节点时,概率关系为有向边,我们可看到,可递推的问题其实就是这个关系图是无环的!!那必须要用方程组解决的问题其实就是存在环!!!! 而且我还要指出的是用高斯消元的时候,要注意误差的问题,最好把式子适当的增大,避免解小数,否则误差太大,估计也会卡题。

《信息学竞赛中概率问题求解初探》

《浅析竞赛中一类数学期望问题的解决方法》

《有关概率和期望问题的研究 》

猜你喜欢

转载自blog.csdn.net/Nrtostp/article/details/81837693