期望dp小结

来源:互联网 发布:淘宝刷销量好评 编辑:程序博客网 时间:2024/06/05 13:26

前言:期望dp状态的定义是较为显然的,但对于状态的转移往往需要一些公式的推导。关键的几点是状态之间的互通性,和状态转移的花费,以及转移的概率


解决期望dp的几个技巧如下:

一.利用期望的线性性质:

E[X+Y]=E[X]+E[Y]
我们所求的期望可以化为多个步骤的期望累和

相关题目:J,L

二.采用逆序的方式:

在目标确定的情况下,可以得知在目标到达目标的期望值为0,然后根据期望的线性性质怎么又是线性性质 得到状态转移方程:
dp[i]+=(dp[j]+cost)*p cost是转移的花费 p是概率 有时要除以(1-转移到自己的概率,但这是又不能单纯地+cost)所以要结合具体情况考虑

相关题目:A,C,D,E

三.利用期望的定义求解:

E[A]=a1*p1+a2*p2+…+an*pn;
a1,a2…an为各种方案(情况)的花费(权值),p1,p2…pn为对应方案(情况)的发生概率 所以在最终方案确定且较少时,只用计算出发生的概率,在最后再乘上权值求累和就好了
相关题目:G,J


实际上,更多的题目需要自己推出公式,并按照推出的规律进行一些状态转移
像这种题,难度就会略高一些:
相关题目:B,H,I


题目选讲:
B:参见Komachi dalao博客
G:
看到220就应该知道可以用状压,
但如果单纯地算每个数被计算出的概率,那么复杂度为n2nT,稳定吃Tle
题目的另外一个特别之处在于:只有|,&,^三个运算符,他们在运算时是不会发生进位的,所以可以直接记录每一位数出现0或1的概率,然后在最后直接概率乘上权值就好了
参见代码:

#include<cstdio>double dp[205][25][2],P[205];int A[205];int B[205][25];char op[205][3];int main(){    int n,cnt=0;    while(~scanf("%d",&n)){        cnt++;        for(int i=1;i<=n+1;i++)            scanf("%d",&A[i]);        for(int i=1;i<=n;i++)scanf("%s",op[i]);        for(int i=1;i<=n;i++)scanf("%lf",&P[i]);        for(int i=0;i<=20;i++)for(int j=0;j<=n;j++)for(int k=0;k<2;k++)dp[j][i][k]=0;        for(int k=1;k<=n+1;k++)            for(int i=0;i<=20;i++)                if(A[k]&(1<<i))B[k][i]=1;                else B[k][i]=0;        for(int i=0;i<=20;i++)            if(B[1][i])dp[0][i][1]=1;            else dp[0][i][0]=1;        for(int i=1;i<=n;i++){            for(int j=0;j<=20;j++){                double p=1-P[i];                if(op[i][0]=='&'){                    if(B[i+1][j]==0)dp[i][j][0]+=(dp[i-1][j][0]+dp[i-1][j][1])*p;                    else {                        dp[i][j][1]+=dp[i-1][j][1]*p;                        dp[i][j][0]+=dp[i-1][j][0]*p;                    }                }                else if(op[i][0]=='|'){                    if(B[i+1][j]==0){                        dp[i][j][0]+=dp[i-1][j][0]*p;                        dp[i][j][1]+=dp[i-1][j][1]*p;                    }                       else dp[i][j][1]+=(dp[i-1][j][1]+dp[i-1][j][0])*p;                }                else if(op[i][0]=='^'){                    if(B[i+1][j]==0){                        dp[i][j][0]+=dp[i-1][j][0]*p;                        dp[i][j][1]+=dp[i-1][j][1]*p;                    }                    else {                        dp[i][j][0]+=dp[i-1][j][1]*p;                        dp[i][j][1]+=dp[i-1][j][0]*p;                    }                }                dp[i][j][1]+=dp[i-1][j][1]*P[i];                dp[i][j][0]+=dp[i-1][j][0]*P[i];            }        }        double ans=0;        for(int j=0;j<=20;j++)            ans+=dp[n][j][1]*(1<<j);        printf("Case %d:\n%.6f\n",cnt,ans);    }    return 0;}

J:
可以知道,对于m道题的每n道题,会对应不同的n个人
那么可以直接对这n个人对应n道题进行状压dp,复杂度Tm2n
根据期望的线性性质,我们可以把每个阶段的期望值都加起来,然后就可以得到答案

#include<cstdio>double dp[15][1200];double P[15][1005];template <class _> _ max(_ x,_ y){return x>y?x:y;}template <class _> _ min(_ x,_ y){return x<y?x:y;}int main(){    int T,t=0;    scanf("%d",&T);    while(T--){        int n,m;        scanf("%d %d",&n,&m);        for(int i=0;i<n;i++)            for(int j=1;j<=m;j++)                scanf("%lf",&P[i][j]);        double ans=0;        int cnt=(m-1)/n+1,tta=(1<<n)-1;        for(int k=0;k<cnt;k++){            int len=min(m,k*n+n);            len-=k*n;            for(int i=0;i<=len;i++)for(int j=0;j<=tta;j++)dp[i][j]=-1;            dp[0][0]=0;            for(int i=1;i<=len;i++){//k*n+i                for(int j=0;j<=tta;j++){                    if(dp[i-1][j]==-1)continue;                    for(int h=0;h<n;h++){                        if((j&(1<<h)))continue;                        dp[i][j|(1<<h)]=max(dp[i][j|(1<<h)],dp[i-1][j]+P[h][k*n+i]);                    }                }            }            double sum=0;            for(int i=0;i<=tta;i++)                if(dp[len][i]>sum)sum=dp[len][i];            ans+=sum;        }        printf("Case #%d: %.5f\n",++t,ans);    }    return 0;}

L:
仍是根据期望的线性性质,求被覆盖点的期望,可以化简为求所有点被覆盖的期望的累和。然后利用容斥,算出点不被覆盖的概率p,然后1pk即为该点被覆盖的概率,也是该点的期望值(由于贡献为1)

#include<cstdio>int main(){    int T,t=0;    scanf("%d",&T);    while(T--){        int n,m,k;        scanf("%d%d%d",&n,&m,&k);        long long tmp=1ll*n*n*m*m;        double ans=0;        for(int i=1;i<=n;i++){            for(int j=1;j<=m;j++){                long long s=0;                s+=1ll*n*n*(j-1)*(j-1);                s+=1ll*m*m*(i-1)*(i-1);                s+=1ll*n*n*(m-j)*(m-j);                s+=1ll*m*m*(n-i)*(n-i);                s-=1ll*(i-1)*(i-1)*(j-1)*(j-1);                s-=1ll*(i-1)*(i-1)*(m-j)*(m-j);                s-=1ll*(n-i)*(n-i)*(m-j)*(m-j);                s-=1ll*(n-i)*(n-i)*(j-1)*(j-1);                 double p=1.0*s/tmp;                double d=1;                for(int h=1;h<=k;h++)d=d*p;                ans+=1-d;            }        }        printf("Case #%d: %.f\n",++t,ans);    }    return 0;}